POJ 2288 Islands and Bridges 状态压缩DP

题意:给出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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值