POJ 2288 Islands and Bridges - 状压dp【TSP】

来源:互联网 发布:房屋装修效果图软件 编辑:程序博客网 时间:2024/05/23 18:33

题目描述

题目大意:

  • 给出一个带点权的无向图,在图上找哈密顿路,使其价值最大,并统计价值最大的哈密顿路的条数.同一条路正走和反走看作同一种方案.
  • 路径的价值分为三部分:
    (1).路径上所有点的点权和.
    (2).路径上所有边的价值和.对于路径C上相邻的两点Ci和Ci+1,它们之间的路径价值为v[Ci]*v[Ci+1].
    (3)对于路径上的连续三点Ci-1,Ci,Ci+1,如果Ci-1与Ci+1之间有边,那么可以另有附加价值v[Ci-1]*v[Ci]*v[Ci+1].

分析:

TSP问题的变形。
因为关系到连续三点的价值,在TSP问题的原有状态表示上加一维。
dp[S][i][j]:状态为S(一个二进制数,第k位上的1表示点k已走过,0表示点k未走过) , 最后经过的两个点为i,j。
dp[S][j][k]=max{dp[S][j][k],dp[S’][i][j]+w[k]+w[k]*w[j]+(i,j,k组成三角形)?w[i]*w[j]*w[k] : 0

初始状态:dp[0][i][j]=w[i]+w[j]+w[i]*w[j]
目标状态:max{dp[(1<< n )-1][a][b]}
方案数在dp的时候顺便算出来就好了。

注意:
1.是哈密顿路,不是哈密顿回路
2.由于“同一条路正走和反走看作同一种方案.”,最后path要除以2
3.算方案数要用long long

#include<cstdio>#include<cstring>#define MAXN 13int n,m,mat[MAXN+10][MAXN+10],dp[(1<<MAXN)+10][MAXN+10][MAXN+10],S,w[MAXN+10];long long way[(1<<MAXN)+10][MAXN+10][MAXN+10];void read(){    int x,y;    scanf("%d%d",&n,&m);    memset(mat,0,sizeof mat);    for(int i=0;i<n;i++)        scanf("%d",&w[i]);    for(int i=1;i<=m;i++){        scanf("%d%d",&x,&y);        if(x==y)            continue;        x--,y--;        mat[x][y]=mat[y][x]=true;    }    S=(1<<n)-1;}void DP(){    memset(dp,-1,sizeof dp);    memset(way,0,sizeof way);    for(int i=0;i<n;i++)        for(int j=0;j<n;j++)            if(mat[i][j]){                dp[(1<<i)|(1<<j)][i][j]=w[i]+w[j]+w[i]*w[j];                way[(1<<i)|(1<<j)][i][j]=1;            }    for(int s=0;s<=S;s++)        for(int i=0;i<n;i++){            if(!(s&(1<<i))) continue;            for(int j=0;j<n;j++){                if(!mat[i][j]||!(s&(1<<j))||dp[s][i][j]==-1)                    continue;                for(int k=0;k<n;k++){                    if((s&(1<<k))||!mat[j][k]) continue;                    int t=dp[s][i][j]+w[k]+w[k]*w[j]+((mat[i][k])?w[i]*w[j]*w[k]:0);                    if(dp[s|(1<<k)][j][k]<t)                        dp[s|(1<<k)][j][k]=t,way[s|(1<<k)][j][k]=way[s][i][j];                    else if(dp[s|(1<<k)][j][k]==t)                        way[s|(1<<k)][j][k]+=way[s][i][j];                }            }        }    int ans=0;    long long path=0;    for(int i=0;i<n;i++)        for(int j=0;j<n;j++)            if(mat[i][j]){                if(ans<dp[S][i][j])                    ans=dp[S][i][j],path=way[S][i][j];                else if(ans==dp[S][i][j])                    path+=way[S][i][j];            }    printf("%d %I64d\n",ans,path/2);}int main(){    int T;    scanf("%d",&T);    while(T--){        read();        if(n==1){            printf("%d 1\n",w[0]);            continue;        }        DP();    }}
0 0