AcWing 1194. 岛和桥

(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;
}
原文地址:https://www.cnblogs.com/dmoransky/p/12984281.html