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;
}
posted @ 2025-05-09 19:41  quanjun  阅读(16)  评论(0)    收藏  举报