No.21 -POJ2288 状压dp求汉密顿回路

// ShellDawn
// POJ2288
// No.21

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<queue>
#define MM(x) memset(x,0,sizeof(x))
#define LL long long
using namespace std;

#define maxn 15
#define maxm (1<<13)
int n;
LL V[maxn];
LL dp[maxm][maxn][maxn];
bool E[maxn][maxn];
LL way[maxm][maxn][maxn];

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        int m;
        scanf("%d%d",&n,&m);
        // 城市编号从0开始,方便状态
        for(int i=0;i<n;i++){
            scanf("%I64d",&V[i]);
        }
        MM(E);
        for(int i=0;i<m;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            E[a-1][b-1] = E[b-1][a-1] = true;
        }
        if(n==1){
            printf("%I64d 1\n",V[0]);
            continue;
        }
        MM(dp);MM(way);
        // 找两个岛,初始化状态
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                if(!E[i][j] || i==j) continue;
                int s = (1<<i) | (1<<j) ;
                dp[s][i][j] = dp[s][j][i] = V[i] * V[j] + V[i] + V[j];
                //printf("(%d %d %I64d)\n",i,j,dp[s][i][j]);
                way[s][i][j] = way[s][j][i] = 1;
            }
        }
        // 状态转移,dp三维度的三重枚举
        for(int s = 3 ; s<(1<<n);s++){
            for(int i=0;i<n;i++){
                // 用i去找新点
                if( ! (s & (1<<i)) ) continue;
                for(int j=0;j<n;j++){
                    // 不能选相同点
                    if(i==j) continue;
                    // 若状态不合法
                    if(dp[s][i][j] == 0) continue;
                    // 点j不在状态中
                    if(!(s & (1<<j)) ) continue;
                    // 找扩展点
                    for(int k=0;k<n;k++){
                        if(s & (1<<k) || !E[i][k] ) continue;
                        int news = s|(1<<k);
                        LL t = dp[s][i][j] + V[i]*V[k] + V[k];
                        // 存在三角
                        if(E[k][j]) t += V[i]*V[j]*V[k];                                
                        //
                        if(dp[news][k][i] == t){
                            way[news][k][i] += way[s][i][j];
                        }
                        else if(dp[news][k][i] < t){
                            dp[news][k][i] = t;
                            way[news][k][i] = way[s][i][j];
                        }   
                    }
                }
            }
        }
        int s = (1<<n) - 1;
        LL ans = 0;
        LL maxway = 0;
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                // 不是回路
                if(!E[i][j]) continue;
                //printf("<%d %d %d>\n",i,j,way[s][i][j]);
                if(dp[s][i][j] > ans){
                    ans = dp[s][i][j];
                    maxway = way[s][i][j];
                }
                else if(dp[s][i][j] == ans){
                    maxway += way[s][i][j];
                }
            }
        }
        printf("%I64d %I64d\n",ans,maxway/2);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值