之前考的时候想不到。今日抽空解决一下。
题意:N个人各指认一个人是好人还是坏人。其中有两个坏人,剩下的都是好人。而且必然是一个好人和一个坏人说的是谎话。给出他们互相指认的信息,请判断出哪两个是坏人。如果由多种答案,输出最小标号的组合。
难点:当初看到数据量N小于等于200,就知道使用暴力。但是没想到怎么去暴力。
当时吧以为人物之间的指认关系是像一张拓扑图一样,所以某个人说的如果是谎话的话后面的节点(后续的指认)都会反过来,就理所当然的以为是搜索的题目了。但这样子加入暴力枚举那两个人是狼,就没想好怎么去寻找拓扑图的开始节点。
昨天重拾,发现自己想多了。这个题目并不是一个拓扑图。因为如果A说B是好人,B说C是好人。如果A是说了谎,并不会改变B说的话的确信度。我自己坑了我自己,想复杂了。
这个题目中设定了好人和坏人;撒谎的人和没有撒谎的人。这两个概念是独立的,不要混在一起,是考验逻辑呀。
所以解决方案是,枚举两个人是坏人,如此一来坏人的身份已经全部确定,接下来如果有哪个人A说某个人B是坏人,而这个被告B并不是我们枚举的坏人,那么这个A就是在扯淡,就是说谎的人!如果哪个人A说某个人B是好人,但是这个人B分明就是我们已经假定的坏人,那这个A又在扯淡,也是说谎的人!
如此以来,每次枚举两个坏人的情况下,我们可以分别确定出在这种情形下撒谎的人的个数。
题目说,撒谎的只有两人,而且一人是好人一人是坏人。所以我们分别统计各好人和坏人撒谎的人数是否都为1,若是即为答案。
Code:
#include<bits/stdc++.h>
using namespace std;
#define inf 209
#define INF 0x3f3f3f3f
int man[inf];//输入指控
int judge(int v,int wolf1,int wolf2)//返回1则在说谎
{
int u=man[v];
int pos=abs(u);
if(u<0&&pos!=wolf1&&pos!=wolf2)return 1;//说别人坏人但是对方是好人
if(u>0&&(pos==wolf1||pos==wolf2))return 1;//说对方好人但对方是坏人
return 0;
}
int main()
{
int i,j,k;
int n;
int sum1,sum2;//说谎者:狼-人
cin>>n;
for(i=1;i<=n;i++)cin>>man[i];
for(i=1;i<n;i++)//狼1
for(j=i+1;j<=n;j++)//狼2
{
sum1=sum2=0;
for(k=1;k<=n;k++)
{
sum1+=judge(k,i,j)&&(k==i||k==j);//狼说谎
sum2+=judge(k,i,j)&&k!=i&&k!=j;//村民说谎
}
if(sum1==1&&sum2==1)
{
printf("%d %d\n",min(i,j),max(i,j));
return 0;
}
}
printf("No Solution\n");
return 0;
}