44 P10963 Islands and Bridges 题解
Island and Bridges
题面
给定一张有 \(n\) 个点, \(m\) 条边的无向图,每个点有权值 \(v_i\)
哈密顿路径为访问每个点恰好一次的路径
定义一个哈密顿路径的权值为以下三个价值的和
- 每个岛的权值之和
- 哈密顿路径中的每条边连接的相邻点的权值乘积之和
- 哈密顿路径中能成环的连续三个点的权值乘积之和
任务1:找出哈密顿路径价值最大值
任务2:找到满足最大价值的路径有多少条
\(1 \le n \le 13, \ 1 \le m \le \frac {n(n - 1)} 2\)
题解
这道题的思路并不难想,但是这道题任务2我搞错了
由于 \(n\) 的数据范围很小,并且路径这个东西不好统计
所以我们维护一个集合表示已经走过的点,因为要计算第二种和第三种价值,所以我们还需要记这条路径最后的两个点
所以设 \(f(s,i,j)\) 表示走过的点的集合为 \(s\) ,倒数第二个点为 \(i\) ,最后一个点为 \(j\) 的最大价值
转移
\[f(s,j,k) = \max \{ f(s \oplus 2^{k - 1},i,j) + calc \}
\]
还有个问题是路径条数怎么算?
我一开始的想法是直接看最后的答案和最大值相等的 \(f(2^n - 1, i, j)\) 有多少个,然后再除以2
但是错了,后来看题解才意识到 \(f(2^n - 1, i, j)\) 可能从多个状态转移
所以它代表的不是一种情况,而是很多种情况
所以我们再记 \(g(s,i,j)\) 表示走过的点的集合为 \(s\) ,倒数第二个点为 \(i\) ,最后一个点为 \(j\) ,最大价值的方案数,这样就能够正确统计方案数了
时间复杂度为 \(O(2^nn^3)\)
code
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
const int N = 15, M = (1 << 13) + 10;
int n, m;
int a[N][N], val[N];
int f[M][N][N];
ll g[M][N][N];
void solve () {
//初始化
memset (a, 0, sizeof a);
//读入
cin >> n >> m;
for (int i = 1; i <= n; i ++) {
cin >> val[i];
}
for (int i = 1; i <= m; i ++) {
int x, y;
cin >> x >> y;
a[x][y] = a[y][x] = 1;
}
if (n == 1) {
cout << val[1] << ' ' << 1 << endl;
return;
}
//初始化
memset (f, -0x3f, sizeof f);
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= n; j ++) {
if (i == j) continue;
if (!a[i][j]) continue;
int s = (1 << i - 1) | (1 << j - 1);
f[s][i][j] = val[i] + val[j] + val[i] * val[j];
g[s][i][j] = 1;
}
}
//状压dp
for (int s = 1; s < (1 << n); s ++) {
for (int i = 1; i <= n; i ++) {
if (!(s & (1 << i - 1))) continue;
for (int j = 1; j <= n; j ++) {
if (!(s & (1 << j - 1))) continue;
for (int k = 1; k <= n; k ++) {
if (!(s & (1 << k - 1))) continue;
if (i == j || j == k || i == k) continue;
if (!a[i][j] || !a[j][k]) continue;
int v = val[k] + val[j] * val[k] + a[i][k] * val[i] * val[j] * val[k];
if (f[s][j][k] < f[s ^ (1 << k - 1)][i][j] + v) {
f[s][j][k] = f[s ^ (1 << k - 1)][i][j] + v;
g[s][j][k] = g[s ^ (1 << k - 1)][i][j];
} else if (f[s][j][k] == f[s ^ (1 << k - 1)][i][j] + v) {
g[s][j][k] += g[s ^ (1 << k - 1)][i][j];
}
}
}
}
}
int ans = 0;
ll cnt = 0;
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= n; j ++) {
if (i == j) continue;
if (ans < f[(1 << n) - 1][i][j]) {
ans = f[(1 << n) - 1][i][j];
cnt = g[(1 << n) - 1][i][j];
} else if (ans == f[(1 << n) - 1][i][j]) {
cnt += g[(1 << n) - 1][i][j];
}
}
}
cout << ans << ' ' << cnt / 2 << endl;
}
int main () {
int T;
cin >> T;
while (T --) {
solve ();
}
return 0;
}

浙公网安备 33010602011771号