洛谷 P1039 [NOIP2003 提高组] 侦探推理

题目来源于:洛谷

原题点这里

题目本质:模拟,字符串,差分,模拟

题目思路:

记录每一句话是谁说的以及这句话的内容,可以用map存人名对应的下标,我们枚举每一个人i,假设i是罪犯,然后枚举今天是星期几,用day表示 。然后判断有没有矛盾。进行每一次判断的时候,先使所有人的状态不确定,也就是不知道他们会说真话假话。TF[a]==-1是不确定,TF[a]=1是说真话,TF[a]=0是说假话。T是说真话的人数,F是说假话的人数 ,设罪犯为 i ,设flag为这句话是真话还是假话,flag=1是真话,flag=0是假话 。id是说这句话的人 。枚举每一句话,看一下id以前的状态,如果状态不确定(TF==-1),就TF[id]=flag。否则,如果和以前状态一样(TF[id]==flag),就没有矛盾,TF[id]!=flag就是出现了矛盾(因为一个人始终直说一种话),判断不出来了,直接return去枚举下一个人是罪犯 ,如果F>n或者T>m-n了,也就是说假话的人数超过了题目中给的人数,矛盾,return。如果找到了不止一个罪犯,输出"Cannot Determine",直接exit(0) 。

完整代码如下:

#include<bits/stdc++.h>
using namespace std;
string S[10] = {
	"Today is Sunday.",
	"Today is Monday.",
	"Today is Tuesday.",
	"Today is Wednesday.",
	"Today is Thursday.",
	"Today is Friday.",
	"Today is Saturday.",
};
int m, n, p;
int T, F, ans;
int TF[25];
string s[25], name, a;
struct Sen {
	int id;
	string s;
} sen[105];
map<string, int> ma;
bool judgeTF(int id, bool flag) {	//看一下有没有冲突,return 1 表示有冲突
	if (TF[id] == -1) {	//状态不确定
		TF[id] = flag;	//赋状态
		if (flag){	//说真话的人数++
			++T;
		}
		else{	//说假话的人数++
			++F;
		}
	} 
	else{
		return TF[id] != flag;	//和之前的一不一样,一样返回0,不一样返回1
	}
	if (F > n || T > m - n)	{//说假话的人比n多或者是说真话的人比m-n多
		return 1;
	}
	return 0;
}
void judge(int id, string day) {
	memset(TF, -1, sizeof(TF));	//所有人都不知道说的是真话假话
	T = F = 0;		//说真话、假话人数置0
	string tmp;
	for (int i = 1; i <= p; ++i) {
		int pos = sen[i].s.find("I am guilty.");	//pos为-1则没说这句话
		if (~pos) {
			if (judgeTF(sen[i].id, sen[i].id == id)){	//因为我们假设了id是罪犯,所以不是id的人就不是罪犯,就是在说假话
				return;
			}
		}
		pos = sen[i].s.find("I am not guilty");
		if (~pos) {
			if (judgeTF(sen[i].id, sen[i].id != id)){
				return;
			}
		}
		pos = sen[i].s.find(" is guilty.");
		if (~pos) {
			tmp = sen[i].s;
			tmp.erase(pos, 11);
			if (judgeTF(sen[i].id, ma[tmp] == id))
				return;
		}
		pos = sen[i].s.find(" is not guilty.");
		if (~pos) {
			tmp = sen[i].s;
			tmp.erase(pos, 15);
			if (judgeTF(sen[i].id, ma[tmp] != id))
				return;
		}
		pos = sen[i].s.find("Today is ");
		if (~pos) {
			if (judgeTF(sen[i].id, sen[i].s == day))
				return;
		}
	}
	if (ans && ans != id) {	//找到了不止一个罪犯
		puts("Cannot Determine");	//不能确定
		exit(0);
	}
	ans = id;		//id是罪犯
}
int main() {
	scanf("%d%d%d", &m, &n, &p);
	for (int i = 1; i <= m; ++i) {
		cin >> s[i];
		ma[s[i]] = i;		//存名字标号
	}
	for (int i = 1; i <= p; ++i) {
		cin >> name;		//输入说话者
		name.erase(name.length() - 1, 1);		//把后边的冒号搞掉
		getline(cin, a);
		a.erase(0, 1);	//把前边的空格搞掉
		if (a[a.length() - 1] == '\n' || a[a.length() - 1] == '\r')	//把坑爹的换行符搞掉
			a.erase(a.length() - 1, 1);
		sen[i].id = ma[name];		//存说话者
		sen[i].s = a;		//存说话内容
	}
	for (int i = 1; i <= m; ++i) {	//假设第i个人是罪犯
		for (int j = 0; j < 7; ++j) {	//假设今天是S[j]天
			judge(i, S[j]);
		}
	}
	if (!ans) {	//找不到罪犯
		puts("Impossible");
	} else {
		cout << s[ans];	//罪犯名字
	}
	return 0;
}

思路借鉴题解 P1039 【侦探推理】 - 洛谷专栏 (luogu.com.cn)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值