POJ 2288 Islands and Bridges 状态压缩DP

来源:互联网 发布:pl2303hx数据手册 编辑:程序博客网 时间:2024/06/05 03:00

题意:给出n个岛和它们之间联通的m条路径,给出权值的计算方法,求权值最大哈密顿回路的权值和数量。
n<=13.
权值的计算方法为:
1.经过的所有点的权值之和;
2.经过的连续的两个点的权值的乘积;
3.能够组成三角形的三个点的权值的乘积。
根据岛是否走过最多有(1<<13)种状态。
dp[s][i][j] = max{ dp[s][i][j] , dp[s’][j][k] + tmp };
tmp = val[i]*val[j] + val[i];
if(i,j,k能够组成三角形) tmp += (val[i]*val[j]*val[k]);

对于路径的数量,如果更新dp[s][i][j],则更新num[s][i][j] = num[s’][j][k];
如果不更新dp[s][i][j],但是 dp[s][i][j] == dp[s’][j][k] + tmp,
则num[s][i][j] += num[s’][j][k].
路径数量最后要除以2,因为反向的路径是一样的,不能重复计算。

s表示当前的状态,i表示当前所在的岛,j表示上一步走的岛,k是所有岛中任意符合要求的岛。

#include <iostream>#include <cmath>#include <algorithm>#include <cstring>using namespace std;const int n = 13;const int maxn = 1<<13;int edge[n][n];int dp[maxn][n][n];int val[n];long long num[maxn][n][n];int N,M;int main(){    int T;    cin>>T;    while(T--){        cin>>N>>M;        for(int i = 0;i < N;i++) cin>>val[i];        int x,y;        memset(edge,0,sizeof(edge));        for(int i = 0;i < M;i++){            cin>>x>>y;            edge[x-1][y-1] = edge[y-1][x-1] = 1;        }        if(N == 1) {cout<<val[0]<<" "<<"1"<<endl;continue;}        memset(dp,-1,sizeof(dp));        memset(num,0,sizeof(num));        for(int i = 0;i < N;i++)            for(int j = 0;j < N;j++){                if(i != j&&edge[i][j]){//初始化dp和num,                    //将走过任意一条边能够到达的状态的dp和num初始化。                    dp[(1<<i)|(1<<j)][i][j] = val[i] + val[j] + val[i] * val[j];                    num[(1<<i)|(1<<j)][i][j] = 1;                }            }        for(int s = 0;s < (1<<N);s++){//枚举状态            for(int i = 0;i < N;i++){                if((s&(1<<i)) == 0) continue;                for(int j = 0;j < N;j++){                    if(((s&(1<<j))==0)||i == j||!edge[i][j]) continue;                    for(int k = 0;k < N;k++){                        if(((s&(1<<k))==0)||i == k||j == k) continue;                        int newS = s-(1<<i);                        if(dp[newS][j][k] == -1) continue;//若为-1,                                                 //则表示不存在路径从第k座岛到达第j座岛                        int tmp = val[i]*val[j]+val[i];//求出tmp值                        if(edge[i][k]) //i,k之间有边,表示能够组成三角形,更新tmp                            tmp += val[i] * val[j] * val[k];                        if(dp[s][i][j] < dp[newS][j][k] + tmp){                            dp[s][i][j] = dp[newS][j][k] + tmp;                            num[s][i][j] = num[newS][j][k];                        }                        else if(dp[s][i][j] == (dp[newS][j][k] + tmp)){                            num[s][i][j] += num[newS][j][k];                        }                    }                }            }        }        long long ans = -1,Num = 0;        int p = (1<<N) - 1;        for(int i = 0;i < N;i++){            for(int j = 0;j < N;j++){                if(i == j||!edge[i][j]) continue;                if(dp[p][i][j] > ans){                    ans = dp[p][i][j];                    Num = num[p][i][j];                }                else if(dp[p][i][j] == ans){                    Num += num[p][i][j];                }            }        }        if(ans == -1) cout<<"0"<<" "<<"0"<<endl;        else cout<<ans<<" "<<Num/2<<endl;    }    return 0;}
0 0
原创粉丝点击