【CF1594D】D. The Number of Imposters(并查集、二分图染色)

这篇博客探讨了一种基于二分图的算法来解决逻辑推理问题。题目涉及n个人,其中一些是好人(总是说真话),一些是坏人(总是说假话)。给出m个关于某人是否为好人的陈述后,任务是确定是否存在矛盾,并找到无矛盾情况下好人的最大数量。博主介绍了如何构建二分图,并通过染色法判断图是否为二分图,从而确定答案。代码示例展示了如何实现这一算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

冒牌货数量

二分图好题

题意:

  • 有 n 个人,每个人身份只可能是 好人 或者 坏人好人只说真话,坏人只说假话。

  • 有 m 个言论, i 、 j 、 c i 、j、c ijc ,表示 i 人 说 j 人 是 好人 或者 坏人( 也就是 c )。

  • 问给定的言论是否有冲突,如果没有冲突求 好人的最大数量

思路:

  • 首先观察可以发现,当言论中 c 为 好人时,两方身份肯定一样,反之当言论 c 为 坏人时,两方身份相反。

  • 一开始想用并查集处理冲突,但发现很难统计人数。 正解是神中神的 建造二分图

  • 如果两人身份不同就在他们之间连一条边(寓意相邻两点颜色不同)

  • 反之如果两人身份相同,我们建一个虚点,让两点连接虚点(寓意两点不相邻颜色相同)。

  • 显然最后我们只需要染色法判断这个图是不是二分图就可以知道有无冲突。如果无冲突,每个连通块记录最多的一种颜色即可。

C o d e : Code: Code:

#include<bits/stdc++.h>
#include<unordered_map>
#define mem(a,b) memset(a,b,sizeof a)
#define cinios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
#define sca scanf
#define pri printf
#define forr(a,b,c) for(int a=b;a<=c;a++)
#define rfor(a,b,c) for(int a=b;a>=c;a--)
#define all(a) a.begin(),a.end()
#define oper(a) (operator<(const a& ee)const)
#define endl "\n"
using namespace std;

typedef long long ll;
typedef pair<int, int> PII;

double DNF = 1e17;
const int N = 700010, M = 1000010, MM = 110;
int INF = 0x3f3f3f3f, mod = 1e9 + 7;
ll LNF = 0x3f3f3f3f3f3f3f3f;
int n, m, k, T, S, D, K;
vector<int> e[N];
int color[N], a1, a2;

bool dfs(int x, int c) {
	color[x] = c;
	if (x <= n) { // 不要把虚点颜色算进去了
		if (c == 1)a1++;
		else a2++;
	}
	for (int j : e[x]) {
		if (!color[j]) {
			if (!dfs(j, 3 - c))return false;
		}
		else if (color[j] == color[x])return false;
	}
	return true;
}

void solve() {
	cin >> n >> m;
	forr(i, 1, n + m)e[i].clear(), color[i] = 0;

	string c;
	int a, b;

	int n1 = n;
	forr(i, 1, m) {
		cin >> a >> b >> c;
		if (c == "crewmate") {
			int g = ++n1;//虚点
			e[a].push_back(g), e[g].push_back(a);
			e[g].push_back(b), e[b].push_back(g);
		}
		else {
			e[a].push_back(b);
			e[b].push_back(a);
		}
	}

	int mx = 0;
	forr(i, 1, n1)if (!color[i]) {
		a1 = a2 = 0;
		if (!dfs(i, 1)) { //判断冲突
			cout << -1 << endl;
			return;
		}
		mx += max(a1, a2);//每次累计最多的一种颜色
	}

	cout << mx << endl;
}

int main() {
	cinios;
	cin >> T;
	while (T--)solve();
	return 0;
}
/*
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值