注意洛谷和codevs是Linux的,Vijos是Windows的,所以洛谷的换行符是’/r’,所以getline(),gets(),getchar(),都可能有一定问题
于是这个可以解决一切
char ch=getchar();
while(ch==10)ch=getchar();getchar();
解决’\r’,多余空格,换行符一系列问题
来自julao
很棒棒!!!的操作 妈妈再也不用担心我卡读入了!!
循环很多注意
思路
1. 读入处理
首先一看就知道是大模拟,所以萌新瑟瑟发抖想了很久要不要写
仔细一想好像略有思路
首先解决读入处理,我选择的是用二维数组存储每人说了些什么,这样记完之后就很方便了,这大概是核心操作,具体看代码,我用的是打表strcmp,截取部分串来判定
2. 然后如何找出犯人
我是先枚举犯人和星期几,这样判定说假说真话会方便很多,依据犯人来看是否说真话,如我的guilty数组;再看是否与今天星期几相符,如我的day数组。
然后直接统计是否符合题目就行
坑点
这么看来这题没啥难度吗!
然而看看讨论,发现事情并没有那么简单
首先有一个人又说自己有罪又说自己没罪。。。。
我好像直接判定impossible了
还有一位叫GUILTY的同志以及一位叫I的同志
还有叫MONDAY-SUNDAY的同志
以及很多很多有意思的东西,大家可以看看讨论
再就是有一句话没说的,可以看做说假话或者说真话,随意处理 这是就是我的zero,在统计时需要枚举一下可能性
还有互相矛盾的,如在Monday B是犯人的假设下:
A:Today is Tuesday.
A: C is not guilty.
虽在星期条件下A是假,但第二句又是真话,故又假又真
矛盾,不成立。
血的教训,如Test3 Test8
代码
- 读入处理(最核心最长的部分)
void update()
{
int len=strlen(s),cnt=0,cnt2=0;
int lena;//第几位名字结束
int ren=0,ren2=0;//
char t[30],name2[300];//因为我用的都是strcmp判定需要提取部分子串来直接判断
memset(t,0,sizeof(t));//论memset的重要性emmmm
memset(name2,0,sizeof(name2));
for(int i=0;i<len;i++)
{
if(s[i]==':'){lena=i-1;break;}
else name2[cnt2++]=s[i];//子串截取一
}
for(int i=1;i<=m;i++)//判断是谁说的
{
int lenb=strlen(name[i]),flag=0;
if(lenb!=lena+1)continue;
else
{
if(!strcmp(name2,name[i]))
{ren=i;break;}
}
}
if(!ren)return;//若没判定到,直接退出(无用信息
for(int i=lena+3;i<len;i++)
if(s[i]!='\r')t[cnt++]=s[i];//截取二,注意是'\r'不是'\n'
int ff=0;
for(int i=0;i<=8;i++)
{
if(!strcmp(t,ch[i]))
{
ff=1;
if(i==0)guilty[ren][ren]=1;//记录此人说谁有无罪,这里我的1表示有罪,2为无罪
else if(i==1)guilty[ren][ren]=2;
else day[ren][i-1]=1;//记录此人说几天星期几
break;
}
}
if(!ff)//继续进一步判定
{
int lenx;
char t2[300],coc=0,t3[20],tot=0;
memset(t2,0,sizeof(t2));
memset(t3,0,sizeof(t3));
for(int i=0;i<strlen(t);i++)
{
if(t[i]==' '){lenx=i-1;break;}
else t2[coc++]=t[i];//截取三
}
for(int i=1;i<=m;i++)
{
if(lenx+1!=strlen(name[i]))continue;
else if(!strcmp(t2,name[i])){ren2=i;break;}
}
if(!ren2)return;//若没判定到,直接退出(无用信息
for(int i=lenx+2;i<strlen(t);i++)
if(t[i]!='\r')t3[tot++]=t[i];//截取四 注意是'\r'不是'\n'
for(int i=0;i<2;i++)
{
if(!strcmp(t3,ch2[i]))
{
if(i==0)guilty[ren][ren2]=1;
else guilty[ren][ren2]=2;
break;
}
}
memset(t2,0,sizeof(t2));
memset(t3,0,sizeof(t3));
}
}
- 找出犯人(坑点最多)
int check(int peo,int today)//枚举罪犯 星期几
{
memset(jia,0,sizeof(jia));
int fake=0;//一定说假话的人
int zero=0;//没说过一句话的人,可以是真是假,随意欺负
for(int i=1;i<=m;i++)
{
int g=0,gg=0;//判定是否没说话
if(guilty[i][peo]==2)jia[i]=1;//承认罪犯无罪,说假话
if(guilty[i][peo]==1)zhen[i]=1;//有罪,真话
//好像有又说自己有罪又说自己无罪的人。。。。。。
if(jia[i])continue;
for(int j=1;j<=m;j++)
if(guilty[i][j]==1&&j!=peo){jia[i]=1;break;}//若有指证别人是犯人的,假
for(int j=1;j<=m;j++)
if(guilty[i][j]==2&&j!=peo){zhen[i]=1;break;}//若有指证别人不是犯人的,真
//关于问啥判两遍,
if(jia[i])continue;
for(int j=1;j<=7;j++)
if(day[i][j]==1&&j!=today){jia[i]=1;break;}//不符合今天星期几的,假
for(int j=1;j<=m;j++)
if(guilty[i][j]==1||guilty[i][j]==2){g=1;break;}//判定是否没说话
for(int j=1;j<=7;j++)
if(day[i][j]==1){gg=1;break;}//判定是否没说话
if(zhen[i]==1&&jia[i]==1)return 0;
if(!gg&&!g)zero++;//没说话,zero++
}
for(int i=1;i<=m;i++)
if(jia[i])fake++;
for(int i=0;i<=zero;i++)
{
if(fake+i==n)return 1;//没说过一句话的人,可以是真是假,
}
return 0;
}
- 主函数
int main()
{
scanf("%d%d%d",&m,&n,&p);
for(int i=1;i<=m;i++)
scanf("%s",name[i]);
char ch=getchar();//因为luogu读入很坑,所以这两句很有必要,解决'\r',多余空格,换行符一系列问题
while(ch==10)ch=getchar();getchar();
for(int i=1;i<=p;i++)
{
gets(s);
update(i);
}
//以上操作后,我们已经记录了谁说今天星期几,谁说谁有罪以及筛掉无用信息
for(int i=1;i<=m;i++)//枚举谁是罪犯
{
for(int j=1;j<=7;j++)//枚举今天星期几
{
if(check(i,j))
{
if(tot1>1&&i!=ans2[cocc]){printf("Cannot Determine");return 0;}//若有多个,输出
if(i!=ans2[cocc])//第一次记录答案
{
tot1++;
strcpy(ans,name[i]);
cocc++;
ans2[cocc]=i;
}
}
}
}
if(!tot1)printf("Impossible");//没查到答案
else printf("%s",ans);
return 0;
}
- 总代码
#include <bits/stdc++.h>
using namespace std;
char ch[20][20]=
{
"I am guilty.",//0
"I am not guilty.",//1
"Today is Monday.",//2
"Today is Tuesday.",//3
"Today is Wednesday.",//4
"Today is Thursday.",//5
"Today is Friday.",//6
"Today is Saturday.",//7
"Today is Sunday.",//8
};//表一
char ch2[10][20]=
{
"is guilty.",
"is not guilty.",
};//表二
int sum,guilty[30][30];//记录谁说谁有,无罪
int day[30][9];//记录谁说今天星期几
int n,m,p;
char name[30][500],s[10001],ans[300];//名字,输入的串,答案记录
int jia[40],zhen[40];//真假数组,后面一个用处不是很大
void update()
{
int len=strlen(s),cnt=0,cnt2=0;
int lena;//第几位名字结束
int ren=0,ren2=0;//
char t[30],name2[300];//因为我用的都是strcmp判定需要提取部分子串来直接判断
memset(t,0,sizeof(t));//论memset的重要性emmmm
memset(name2,0,sizeof(name2));
for(int i=0;i<len;i++)
{
if(s[i]==':'){lena=i-1;break;}
else name2[cnt2++]=s[i];//子串截取一
}
for(int i=1;i<=m;i++)//判断是谁说的
{
int lenb=strlen(name[i]),flag=0;
if(lenb!=lena+1)continue;
else
{
if(!strcmp(name2,name[i]))
{ren=i;break;}
}
}
if(!ren)return;//若没判定到,直接退出(无用信息
for(int i=lena+3;i<len;i++)
if(s[i]!='\r')t[cnt++]=s[i];//截取二,注意是'\r'不是'\n'
int ff=0;
for(int i=0;i<=8;i++)
{
if(!strcmp(t,ch[i]))
{
ff=1;
if(i==0)guilty[ren][ren]=1;//记录此人说谁有无罪,这里我的1表示有罪,2为无罪
else if(i==1)guilty[ren][ren]=2;
else day[ren][i-1]=1;//记录此人说几天星期几
break;
}
}
if(!ff)//继续进一步判定
{
int lenx;
char t2[300],coc=0,t3[20],tot=0;
memset(t2,0,sizeof(t2));
memset(t3,0,sizeof(t3));
for(int i=0;i<strlen(t);i++)
{
if(t[i]==' '){lenx=i-1;break;}
else t2[coc++]=t[i];//截取三
}
for(int i=1;i<=m;i++)
{
if(lenx+1!=strlen(name[i]))continue;
else if(!strcmp(t2,name[i])){ren2=i;break;}
}
if(!ren2)return;//若没判定到,直接退出(无用信息
for(int i=lenx+2;i<strlen(t);i++)
if(t[i]!='\r')t3[tot++]=t[i];//截取四 注意是'\r'不是'\n'
for(int i=0;i<2;i++)
{
if(!strcmp(t3,ch2[i]))
{
if(i==0)guilty[ren][ren2]=1;
else guilty[ren][ren2]=2;
break;
}
}
memset(t2,0,sizeof(t2));
memset(t3,0,sizeof(t3));
}
}
int check(int peo,int today)//枚举罪犯 星期几
{
memset(jia,0,sizeof(jia));
int fake=0;//一定说假话的人
int zero=0;//没说过一句话的人,可以是真是假,随意欺负
for(int i=1;i<=m;i++)
{
int g=0,gg=0;//判定是否没说话
if(guilty[i][peo]==2)jia[i]=1;//承认罪犯无罪,说假话
if(guilty[i][peo]==1)zhen[i]=1;//有罪,真话
//好像有又说自己有罪又说自己无罪的人。。。。。。
if(jia[i])continue;
for(int j=1;j<=m;j++)
if(guilty[i][j]==1&&j!=peo){jia[i]=1;break;}//若有指证别人是犯人的,假
for(int j=1;j<=m;j++)
if(guilty[i][j]==2&&j!=peo){zhen[i]=1;break;}//若有指证别人不是犯人的,真
//关于问啥判两遍,
if(jia[i])continue;
for(int j=1;j<=7;j++)
if(day[i][j]==1&&j!=today){jia[i]=1;break;}//不符合今天星期几的,假
for(int j=1;j<=m;j++)
if(guilty[i][j]==1||guilty[i][j]==2){g=1;break;}//判定是否没说话
for(int j=1;j<=7;j++)
if(day[i][j]==1){gg=1;break;}//判定是否没说话
if(zhen[i]==1&&jia[i]==1)return 0;
if(!gg&&!g)zero++;//没说话,zero++
}
for(int i=1;i<=m;i++)
if(jia[i])fake++;
for(int i=0;i<=zero;i++)
{
if(fake+i==n)return 1;//没说过一句话的人,可以是真是假,
}
return 0;
}
int tot1=0,cocc=0;
int ans2[40];
int main()
{
scanf("%d%d%d",&m,&n,&p);
for(int i=1;i<=m;i++)
scanf("%s",name[i]);
char ch=getchar();//因为luogu读入很坑,所以这两句很有必要,解决'\r',多余空格,换行符一系列问题
while(ch==10)ch=getchar();getchar();
for(int i=1;i<=p;i++)
{
gets(s);
update();
}
//以上操作后,我们已经记录了谁说今天星期几,谁说谁有罪以及筛掉无用信息
for(int i=1;i<=m;i++)//枚举谁是罪犯
{
for(int j=1;j<=7;j++)//枚举今天星期几
{
if(check(i,j))
{
if(tot1>1&&i!=ans2[cocc]){printf("Cannot Determine");return 0;}//若有多个,输出
if(i!=ans2[cocc])//第一次记录答案
{
tot1++;
strcpy(ans,name[i]);
cocc++;
ans2[cocc]=i;
}
}
}
}
if(!tot1)printf("Impossible");//没查到答案
else printf("%s",ans);
return 0;
}
总共172行,函数有大概15个循环
历经千辛万苦终于AC
总之还是比较考验码力和细心程度的题
可以码码练练手
感谢