【洛谷1039】侦探推理(字符串模拟题)
- 有\(m\)个人,其中有一个罪犯。已知\(n\)个人始终说假话,其余人始终说真话。
- 有\(p\)句证词,分下列五种格式:(不属于任何格式的证词需要忽略)
I am guilty.
:我是罪犯。I am not guilty.
:我不是罪犯。XXX is guilty.
:XXX是罪犯。XXX is not guilty.
:XXX不是罪犯。Today is XXX.
:今天是XXX。其中XXX为Monday
~Sunday
。
- 需要找出谁是罪犯,或判断无解或有多种可能。
- \(m\le20,p\le100,|S|\le250\)
字符串模拟
直接枚举谁是罪犯,今天是星期几,判断始终说假话人数是否小于等于\(n\),始终说真话人数是否小于等于\(m-n\)。(之所以这么判是因为可能有只说废话或不说话的人)
判断显然是很简单的,主要涉及一些对字符串的操作。因此这里简单记录一下通过这题学到的几种字符串函数用法:
getline(cin,s)
:读入字符串\(s\)。s.erase(p,l)
:从\(s\)第\(p\)个字符开始删去\(l\)个字符。s.substr(p,l)
:返回从\(s\)第\(p\)个字符开始连续\(l\)个字符构成的子串。
代码:\(O(mp)\)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define M 20
#define P 100
#define S 250
using namespace std;
const string Day[7]={"Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"};
int m,n,p,u[P+5],f[M+5];string g[M+5],w[P+5],s[P+5];map<string,int> id,dt;
I bool U(CI id,bool v) {return id?(f[id]==(v^1)?0:(f[id]=v,1)):1;}//id说话的真假性为v,判是否矛盾
int main()
{
RI i;for(i=0;i^7;++i) dt[Day[i]]=i;
for(scanf("%d%d%d",&m,&n,&p),i=1;i<=m;++i) cin>>g[i],id[g[i]]=i;
for(i=1;i<=p;++i) cin>>w[i],u[i]=id[w[i].substr(0,w[i].size()-1)],
getline(cin,s[i]),s[i].erase(0,1),s[i].erase(s[i].size()-1,1);//注意,因为线上评测问题,getline会多读入一个\n,需要删去末尾字符
RI j,k,t0,t1,ans=0;for(i=1;i<=m;++i) for(j=0;j^7&&ans^i;++j)//枚举罪犯是谁,今天是星期几
{
for(k=1;k<=m;++k) f[k]=-1;for(k=1;k<=p;++k)//枚举每一句证词
{
if(s[k]=="I am guilty."&&!U(u[k],u[k]==i)) goto End;//我是罪犯
if(s[k]=="I am not guilty."&&!U(u[k],u[k]^i)) goto End;//我不是罪犯
if(s[k].size()>10&&s[k].substr(s[k].size()-11,11)==//XXX是罪犯
" is guilty."&&!U(u[k],id[s[k].substr(0,s[k].size()-11)]==i)) goto End;
if(s[k].size()>14&&s[k].substr(s[k].size()-15,15)==//XXX不是罪犯
" is not guilty."&&!U(u[k],id[s[k].substr(0,s[k].size()-15)]^i)) goto End;
if(s[k].size()>10&&s[k].substr(0,9)=="Today is "&&//今天是XXX
dt.count(s[k].substr(9,s[k].size()-10))&&!U(u[k],dt[s[k].substr(9,s[k].size()-10)]==j)) goto End;
}
for(t0=t1=0,k=1;k<=m;++k) !f[k]&&++t0,f[k]==1&&++t1;if(t0>n||t1>m-n) goto End;//如果始终说假话/真话人数不符
if(ans) return puts("Cannot Determine"),0;ans=i;End:;//判多种可能
}return ans?(cout<<g[ans]<<endl,0):puts("Impossible"),0;//判无解
}
待到再迷茫时回头望,所有脚印会发出光芒