蒟蒻君的刷题日记Day11(DP专题T2):CF53E Dead Ends

这篇博客探讨了一道动态规划题目,涉及n≤10的数据规模。作者指出,直接使用状态压栈DP可能会因为顺序不同导致重复计数,从而引发错误答案。解决方案是加入条件判断,确保叶子节点的最高位为最新加入的。博客提供了代码实现,但未通过洛谷平台的审核,作者尝试用集合语言重写代码。最后,博主分享了优化后的代码,并表示这是一道需要深入理解的题目。
摘要由CSDN通过智能技术生成

解题思路

数据规模 n ≤ 10 n\le10 n10 的 dp 题,显然为状压 dp。

状态定义: d p [ i ] [ j ] dp[i][j] dp[i][j] 为联通情况为 i i i,叶子结点情况为 j j j 时的方案数。

状态转移方程:和正常的状压差不多,这里不好描述,详见代码。

边界处理: d p [ 1 < < ( i − 1 ) ] [ 1 < < ( i − 1 ) ] = 1 ( i ∈ [ 1 , n ] ) dp[1<<(i-1)][1<<(i-1)]=1(i \in [1,n]) dp[1<<(i1)][1<<(i1)]=1(i[1,n])

这道紫题真得有那么简单嘛?WA。

如果直接这样 DP,顺序不同的方案也会被重复计算。如以下情况:

在这里插入图片描述

我们发现是因为不同的根节点导致的,所以我们加入一个判断条件:

j j j 加入时当且仅当最高位是最新加入的。

22.2.9 Upd

洛谷上没审核通过,兔队让我用集合的语言写/kk
重新写了一下:

定义 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示目前生成树状态为 i i i 时,其中度数为 1 1 1 的点的状态为 j j j 时的方案数。

对于加边,可以从连到度数为 1 1 1 的点和其它点的情况中取最大值,状态转移方程见代码。

代码实现

#include <bits/stdc++.h>
using namespace std;
const int N = 1 << 10;
int s[N], head[15], cnt;
long long dp[N][N];
struct edge {
	int v, nxt;
} e[205];
inline void add(int u, int v) {
    e[++cnt] = {v, head[u]};
    head[u] = cnt;
}
int main() {
    ios :: sync_with_stdio(0);
    int n, m, k;
    cin >> n >> m >> k;
    while (m--) {
        int u, v;
        cin >> u >> v;
        add(u, v);
        add(v, u);
    }
    int S = (1 << n) - 1;
    for (int i = 1; i <= S; ++i) {
        for (int j = 1; j <= n; ++j) {
			if (i & (1 << j - 1)) {
				++s[i];
			}
        }
    }
	for (int i = 1; i <= n; ++i) {
		dp[1 << i - 1][1 << i - 1] = 1;
    }
	for (int i = 1; i <= S; ++i) {
		for (int j = i; j; --j &= i) {
			if (!dp[i][j]) {
				continue;
			}
			for (int p = 1; p <= n; ++p) {
				if (i & (1 << p - 1)) {
					for (int l = head[p]; l; l = e[l].nxt) {
						int v = e[l].v, t;
						if (i & (1 << v - 1)) {
                            continue;
                        }
                        t = s[i] == 1 ? (i | (1 << v - 1)) : (j & ~(1 << p - 1) | (1 << v - 1));
						if (!(t >> v)) {
                            dp[i | (1 << v - 1)][t] += dp[i][j];
                        }
					}
				}
			}
		}
    }
    long long res = 0;
	for (int i = 1; i <= S; ++i) {
		res += (s[i] == k) * dp[S][i];
	}
    cout << res << '\n';
    return 0;
}

Good Good 贺题,Day Day Up!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蒟蒻一枚

谢谢鸭~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值