AcWing 1194. 岛和桥
\(f[s][i][j]\) 表示一条有向路径(不经过重复点),当前路径点集合为 \(s\),最后两个点是 \(j\) → \(i\) 的最大价值
\(g[s][i][j]\) 类似,不过是方案数。
较为显然的转移,枚举 \(s\) 中的点 \(k (i \not= j \not = k)\):
\(f[s][i][j] = \max(f[s\ \text{xor}\ 2^k][j][k] + a[i] \times a[j] + \text{w}(i,j,k))\)
\(\text{w}(i, j, k)\) 表示 \(i, j, k\) 如果构成一个环的权值,否则为 \(0\)。
方案数也是类似的。
初始化两点路径,即 \(f[2^i\ \text{or}\ 2^j][i][j] = a[i] \times a[j]\)
注意方案数最后要 \(/2\),因为会统计两次(我们规定的是有向)
复杂度
\(O(2^n\times n^3)\) 用 \(\text{lowbit}\) 应该还能省复杂度,但是我不会算
注意特判一个节点的时候
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
typedef long long LL;
const int N = 13;
int n, m, a[N], Log[1 << N], sum;
bool w[N][N];
LL f[1 << N][N][N], g[1 << N][N][N];
void out(int x) {
for (int i = 0; i < n; i++)
printf("%d", x >> i & 1);
}
void inline clear() {
memset(w, false, sizeof w);
memset(g, 0, sizeof g);
memset(f, 0, sizeof f);
sum = 0;
}
int main() {
int T; scanf("%d", &T);
while (T--) {
clear();
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i++) scanf("%d", a + i), Log[1 << i] = i, sum += a[i];
for (int i = 1; i <= m; i++) {
int a, b; scanf("%d%d", &a, &b);
--a, --b;
w[a][b] = w[b][a] = true;
}
if (n == 1) { printf("%d %d\n", a[0], 1); continue; }
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (i != j && w[i][j]) {
f[(1 << i) | (1 << j)][i][j] = a[i] * a[j];
g[(1 << i) | (1 << j)][i][j] = 1;
}
}
}
for (int s = 1; s < (1 << n); s++) {
for (int A = s, i = Log[s & -s]; A ; A -= 1 << i, i = Log[A & -A]) {
for (int B = s ^ (1 << i), j = Log[B & -B]; B; B -= 1 << j, j = Log[B & -B]) {
if (!w[i][j]) continue;
for (int C = s ^ (1 << i) ^ (1 << j), k = Log[C & -C]; C; C -= 1 << k, k = Log[C & -C]) {
if (!w[j][k] || !f[s ^ (1 << i)][j][k]) continue;
LL val = f[s ^ (1 << i)][j][k] + a[i] * a[j] + (w[i][k] ? a[i] * a[j] * a[k] : 0);
if (val > f[s][i][j]) f[s][i][j] = val, g[s][i][j] = g[s ^ (1 << i)][j][k];
else if (val == f[s][i][j]) g[s][i][j] += g[s ^ (1 << i)][j][k];
}
}
}
}
LL res = 0, cnt = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (!g[i][j]) continue;
if (f[(1 << n) - 1][i][j] > res) res = f[(1 << n) - 1][i][j], cnt = g[(1 << n) - 1][i][j];
else if (f[(1 << n) - 1][i][j] == res) cnt += g[(1 << n) - 1][i][j];
}
}
if (!res) puts("0 0");
else printf("%lld %lld\n", res + sum, cnt / 2);
}
return 0;
}