(f[s][i][j]) 表示一条有向路径(不经过重复点),当前路径点集合为 (s),最后两个点是 (j) → (i) 的最大价值
(g[s][i][j]) 类似,不过是方案数。
较为显然的转移,枚举 (s) 中的点 (k (i ot= j ot = k)):
(f[s][i][j] = max(f[s ext{xor} 2^k][j][k] + a[i] imes a[j] + ext{w}(i,j,k)))
( ext{w}(i, j, k)) 表示 (i, j, k) 如果构成一个环的权值,否则为 (0)。
方案数也是类似的。
初始化两点路径,即 (f[2^i ext{or} 2^j][i][j] = a[i] imes a[j])
注意方案数最后要 (/2),因为会统计两次(我们规定的是有向)
复杂度
(O(2^n imes n^3)) 用 ( ext{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
", 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
", res + sum, cnt / 2);
}
return 0;
}