POJ 2288 Islands and Bridges【状压dp】

http://poj.org/problem?id=2288

题意:

在一个无向图中G(V,E)中(|V|<=13),每一个点有一个点权,并有哈密顿路径(指从起点开始不重复经过一点到终点的路径)。

代价:

1.所有的点权之和;

2.哈密顿路径中相邻点点权乘积之和

3.哈密顿路径中相邻的三个点若形成环,则3的答案加上这三个点的乘积。

例如:

分析:

状压dp,状压所有的情况,处理添加一个点进入路径的所有情况

d[k][i][S+{k}]=max{ d[i][j][S]+vk+vk*vi+get(k,i,j)   | j!=i且i,j∈S,k∈S}.

get(k,i,j)在k,i,j三点构成三角形的时候返回vk*vi*vj,否则返回0.

初值:d[i][j][{i,j}]=vi+vj+vi*vj.i!=j.0<=i,j<=n-1.原题中说编号从1到n,程序中我们使用编号从0到n-1.

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m;
int v[15];
int d[15][15][1<<13];///第一维是终点,第二位是起点
long long num[15][15][1<<13];
int g[15][15];///用于保存初始的图 g[i][j]=1 则表示i到j有路 为0则无路
int mi[15];
int base[20000][15];
inline int get(int k,int i,int j)///如果从k到j有边,则返回这个三角形的权值
{
    if(g[k][j])return v[k]*v[i]*v[j];
    return 0;
}
int main()
{
    int temp=1;
    for(int i=0;i<=13;i++)
    {
        mi[i]=temp;
        temp *=2;
    }
    memset(base,0,sizeof(base));
    for(int value=0;value<mi[13];value++)///预先计算base[x][i]=1表示x的二进制第i位是1
    {
        temp = value;
        int p=0;///第p位
        while(temp>0)
        {
            base[value][p++]=temp%2;
            temp/=2;
        }
    }
    int t;
    while(~scanf("%d",&t)&&t)
    {
        while(t--)
        {
            scanf("%d%d",&n,&m);
            for(int i=0;i<n;i++)
                scanf("%d",&v[i]);
            memset(g,0,sizeof(g));
            for(int i=0;i<m;i++)
            {
                int a,b;
                scanf("%d%d",&a,&b);
                a--;b--;
                g[a][b]=g[b][a]=1;
            }
            if(n==1)///只有一个顶点时特殊处理
            {
                printf("%d 1\n",v[0]);
                continue;
            }
            memset(d,-1,sizeof(d));///因为如果初始化为0,那么无法区分非法值和合法值,最后非法值也参与运算直接导致错误的结果.
            memset(num,0,sizeof(num));
            for(int i=0;i<n;i++)///获取d和num的初值
                for(int j=0;j<n;j++)if(i!=j && g[i][j] )///i与j之间有路
                {
                    d[i][j][mi[i]+mi[j]]=v[i]+v[j]+v[i]*v[j];
                    num[i][j][mi[i]+mi[j]]=1;
                }
            for(int S=0;S<mi[n];S++)
            {
                for(int i=0;i<n;i++)if(base[S][i]==1)///经过了i
                {
                    for(int j=0;j<n;j++)if(i!=j && base[S][j]==1 &&g[i][j])///经过节点j且i到j有边
                    {
                        if(d[i][j][S]==-1)continue;     ///该值非法
                        for(int k=0;k<n;k++)if(base[S][k]==0 && g[k][i])    ///没有经过k且从k到i有边,从i走到k
                        {
                            if( d[k][i][S+mi[k]] < d[i][j][S]+v[k]+v[k]*v[i]+get(k,i,j) ) ///特判有三角的特殊情况
                            {
                                d[k][i][S+mi[k]]=d[i][j][S]+v[k]+v[k]*v[i]+get(k,i,j);
                                num[k][i][S+mi[k]] = num[i][j][S];
                            }
                            else if( d[k][i][S+mi[k]] == d[i][j][S]+v[k]+v[k]*v[i]+get(k,i,j) ) ///产生相同最大值,记录次数
                            {
                                num[k][i][S+mi[k]] += num[i][j][S];
                            }
                        }
                    }
                }
            }
            long long sum = 0;///出现的次数
            int max_sum=0;///出现的最大值
            for(int i=0;i<n;i++)
            {
                for(int j=0;j<n;j++)if(i!=j )///j到i的路径
                {
                    if(max_sum < d[i][j][(1<<n)-1])
                    {
                        max_sum=d[i][j][(1<<n)-1];
                        sum = num[i][j][(1<<n)-1];
                    }
                    else if(max_sum == d[i][j][(1<<n)-1])
                    {
                        sum += num[i][j][(1<<n)-1];
                    }
                }
            }
            printf("%d %I64d\n",max_sum,sum/2);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/lml11111/article/details/81951774
今日推荐