真正的骗子(种类并查集)

题目链接

  思路:
    分成两类:1.村民说真话,2.村民说假话。当村民说是好人的时候,有两种情况,他们都是好人和都是坏人。所以将a + (x+y), b + (x+y)a,b合并为一个集合。同理将a,b+(x+y)a+(x+y),b合并为一个集合。
    这个合并的过程我们就可以采用种类并查集的方式来将他们合并起来.

std::function<int(int)> find = [&] (int x) -> int { return x == f[x] ? f[x] : f[x] = find(f[x]); }; auto merge = [&] (int u, int v) -> void { u = find(u), v = find(v); if (u == v) return ; //按秩合并 if (dep[u] > dep[v]) f[v] = u, s[u] += s[v]; else if (dep[u] < dep[v]) f[u] = v, s[v] += s[u]; else f[v] = u, s[u] += s[v], dep[u]++; }; //种类并查集合并 for (int i = 1; i <= n; i++) { int a, b; char op[5]; std::cin >> a >> b >> op; if (op[0] == 'y') merge(a, b), merge(a + res, b + res); else merge(a, b + res), merge(a + res, b); } //统计有多少个集合 int cnt = 0; for (int i = 1, fa; i <= res; i ++) { if ((fa = find(i)) != i) continue; vis[fa] = vis[fa + res] = ++cnt; p[cnt] = s[fa], q[cnt] = s[fa + res]; }

    要判断我们能否知道每个人的身份的话,也就是我们要凑出来有x个人是好人,剩下的y个人是坏人,我们提前已经将所有的人都分成了两个集合,好人集合和坏人集合,那么考虑用dp来将所有的状态来进行转移,类比于背包求解方案数的问题.定义dp[i][j]表示的是前i个连通块中有j个好人的方案是否存在,如果dp[i][j]==1那就是这个方案可行,否则不然.

dp[0][0] = 1; for (int i = 1; i <= cnt; i++) { for (int j = std::min(p[i], q[i]); j <= x; j++) { if (j >= p[i]) dp[i][j] += dp[i - 1][j - p[i]]; if (j >= q[i]) dp[i][j] += dp[i - 1][j - q[i]]; } } if (dp[cnt][x] != 1) { std::cout << "no\n"; continue; } for (int i = cnt; i; i--) { if (dp[i - 1][x - p[i]]) x -= p[i], p[i] = -1; else if (dp[i - 1][x - q[i]]) x -= q[i], p[i] = -2; }

    最后输出方案中的所有的好人

dp[0][0] = 1; for (int i = 1; i <= cnt; i++) { for (int j = std::min(p[i], q[i]); j <= x; j++) { if (j >= p[i]) dp[i][j] += dp[i - 1][j - p[i]]; if (j >= q[i]) dp[i][j] += dp[i - 1][j - q[i]]; } } if (dp[cnt][x] != 1) { std::cout << "no\n"; continue; } for (int i = cnt; i; i--) { if (dp[i - 1][x - p[i]]) x -= p[i], p[i] = -1; else if (dp[i - 1][x - q[i]]) x -= q[i], p[i] = -2; }

    多组测试注意初始化

int res = x + y; memset(dp, 0, sizeof dp); for (int i = 1; i <= res; i++) { f[i] = i, f[i + res] = i + res; dep[i] = dep[i + res] = s[i] = 1; s[i + res] = vis[i] = vis[i + res] = 0; } for (int i = 1; i <= res; i++) s[i] = 1;

__EOF__

本文作者HoneyGrey
本文链接https://www.cnblogs.com/Haven-/p/16633624.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   浅渊  阅读(36)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示