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

题目描述

题目大意:

  • 给出一个带点权的无向图,在图上找哈密顿路,使其价值最大,并统计价值最大的哈密顿路的条数.同一条路正走和反走看作同一种方案.
  • 路径的价值分为三部分:
    (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 13

int 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();
    }
}
posted @ 2016-03-20 11:03  KatarinaYuan  阅读(112)  评论(0编辑  收藏  举报