CF53E. Dead Ends 题解 状压DP
题目链接:https://codeforces.com/contest/53/problem/E
解题思路:
来自 live4m大佬的博客
n<=10,容易想到状压。
d[s][t]表示点集s连接成一棵生成树,其中点集t是end point的方案数。
对于集合d[s][t],枚举集合s内的点i,枚举集合s外的点j,
则d[nt_s][nt_t]+=d[s][t].
d[s][t]状态包含多种生成树,
对于其中的同一种生成树,根据加边顺序的不同,有不同的构造顺序,
这题我们应该忽略加边的顺序,所以对于每个d[s][t],需要首先将d[s][t]/=cnt[t]
这里cnt[t]表示t中二进制1的个数,
因为对于一棵生成树,可以由cnt[t]种生成树转移得来(考虑删掉一个end_point,有cnt[t]种删法),
由于最终加的边是一样的,所以对于这一棵树,方案重复累加了cnt[t]次。
示例程序:
#include <bits/stdc++.h>
using namespace std;
int n, m, K;
bool g[11][11];
long long f[1<<10][1<<10], ans;
bool chk(int s, int i) {
return (s >> i) & 1;
}
int main() {
cin >> n >> m >> K;
for (int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
u--, v--;
g[u][v] = g[v][u] = true;
}
for (int s = 1; s < (1<<n); s++) {
int s_cnt = __builtin_popcount(s);
if (s_cnt == 1)
f[s][s] = 1;
for (int t = s; t; t = s & (t-1)) {
if (!f[s][t])
continue;
int t_cnt = __builtin_popcount(t);
f[s][t] /= t_cnt;
for (int i = 0; i < n; i++) {
if (chk(s, i)) {
for (int j = 0; j < n; j++) {
if (!chk(s, j) && g[i][j]) {
int ns, nt;
ns = s ^ (1<<j);
if (chk(t, i)) {
if (s_cnt == 1) nt = t ^ (1<<j);
else nt = t ^ (1<<i) ^ (1<<j);
}
else nt = t ^ (1<<j);
f[ns][nt] += f[s][t];
}
}
}
}
}
}
for (int i = 1; i < (1<<n); i++)
if (__builtin_popcount(i) == K)
ans += f[(1<<n)-1][i];
cout << ans << endl;
return 0;
}
浙公网安备 33010602011771号