思路:所求的哈密顿路径由三部分组成,一,所有节点的权值之和,二,每条边的权值之和,三,三元环的权值和。设集合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"); } } /* */
