POJ 2288 Islands and Bridges(状压dp)

思路:所求的哈密顿路径由三部分组成,一,所有节点的权值之和,二,每条边的权值之和,三,三元环的权值和。设集合S为拜访的点的集合,当向集合S中加入一个点,与上一条边的起点和终点有关。所以令dp[s][i][j]表示集合S通过边(i, j)加入j点之后的最大权值。

那么 dp[s][i][j] = max(dp[s][i][j], dp[p][k][i] + tmp). p表示未加入j点的集合,tmp表示加入j点之后增加的权值,tmp = v[j] + v[i] * v[j], 如果i, j, k三点构成三元环,那么tmp再加上v[i] * v[j] * v[k].

再用一个数组统计路径数。

注意答案会超过int,要用long long.

具体细节见代码

#include<cstdio>
#include<set>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<map>
#include<queue>
#include<vector>
#include<stack>
#include<string>
#include<sstream>
#include<set>
#include<cmath>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 1e2 + 20;
const double EPS = 1e-8;
const int mod = 1e8;
typedef unsigned long long ull;
typedef long long LL;
int dx[] = {0, 0, -1, 1, -1, -1, 1, 1};
int dy[] = {1, -1, 0, 0, -1, 1, -1, 1};
inline int dcmp(double x, double y){if(fabs(x - y) < EPS) return 0; return x > y ? 1 : -1; }
int n, m;
int g[13][13];
LL dp[1 << 13][13][13];
int num[1 << 13][13][13];
int v[13];
int main(){
    int T;
    scanf("%d", &T);
    while(T--){
        scanf("%d%d", &n, &m);
        for(int i = 0; i < n; ++i){
            scanf("%d", &v[i]);
        }
        memset(g, 0, sizeof g);
        memset(num, 0, sizeof num);
        for(int i = 0; i < m; ++i){
            int x, y;
            scanf("%d%d", &x, &y);
            g[x - 1][y - 1] =  g[y - 1][x - 1] = 1;
        }
        memset(dp, -1, sizeof dp);
        for(int i = 0; i < n; ++i){
            for(int j = 0; j < n; ++j){
                if(i == j) continue;
                if(g[i][j] == 0) continue;
                dp[1 << i | (1 << j)][i][j] = v[i] + v[j] + v[i] * v[j];
                num[1 << i | (1 << j)][i][j] = 1;
            }
        }
        LL ans = -1;
        for(int i = 0; i < (1 << n); ++i){
            for(int j = 0; j < n; ++j){
                if(i & (1 << j)) continue;
                for(int k = 0; k < n; ++k){
                    if(j == k) continue;
                    if(!(i & (1 << k)))continue;
                    if(g[k][j] == 0) continue;
                    for(int l = 0; l < n; ++l){
                        if(k == l)continue;
                        if(l == j) continue;
                        if(!(i & (1 << l)))continue;
                        if(dp[i][l][k] == -1)continue;
                        LL s = v[j] + v[k] * v[j] + dp[i][l][k];
                        if(g[j][l]) s += v[k] * v[j] * v[l];
                        if(dp[i | (1 << j)][k][j] < s){
                            dp[i | (1 << j)][k][j] = s;
                            num[i | (1 << j)][k][j] = num[i][l][k];
                        }
                        else if(dp[i | (1 << j)][k][j] == s){
                            num[i | (1 << j)][k][j] += num[i][l][k];
                        }
                    }
                }
            }
        }
        LL cnt = 0;
        int t = (1 << n) - 1;
        for(int j = 0; j < n; ++j){
            for(int k = 0; k < n; ++k){
                if(j == k) continue;
                ans = max(ans, dp[t][j][k]);
            }
        }
        for(int j = 0; j < n; ++j){
            for(int k = 0; k < n; ++k){
                if(j == k) continue;
                if(dp[t][j][k] == ans) cnt += num[t][j][k];
            }
        }
        cnt /= 2;
        if(n == 1) ans = v[0], cnt = 1;
        if(ans != -1)
        printf("%lld %lld\n", ans, cnt);
        else
            printf("0 0\n");

    }
}
/*

*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值