有一群好人和一群坏人,好人永远说真话,坏人永远说假话。现给出一组话,问能否唯一确定每个人是好人还是坏人。
Input
对于每组数据 第一行包括三个整数 n p q 表示话语的数量 好人和坏人的数量 接下来n行 形式如 x1 y1 yes/no 表示x1 说y1 是 好人/坏人 以0 0 0 结尾。 n<1000 ; p1 ,p2<300
Output
如果能唯一确定,输出所有的好人(每行一个,增序,以end结尾),否则输出no;
Sample Input
2 1 1
1 2 no
2 1 no
3 2 1
1 1 yes
2 2 yes
3 3 yes
2 2 1
1 2 yes
2 3 no
5 4 3
1 2 yes
1 3 no
4 5 yes
5 6 yes
6 7 no
0 0 0
Sample Output
no
no
1
2
end
3
4
5
6
end
先用并查集求出若干个大集合,大集合分2个不同种类人个数的小集合
从每个大集合中取1数,代表这个类型的人数。
如果最后能恰好取到q,且只有一种就行。
.。。。。
比较难的地方就是如何存标号,最后要升序输出 ,我们可以用map进行对应编号,最后再升序扫描输出就行了。
看看代码应该就差不多了
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<string>
#include<map>
using namespace std;
const int M = 1000 +100;
int fa[M],d[M];
int judge[M];//dp完找路径时,选择的集合种类
int get(int x)
{
if(x==fa[x])return x;
int root=get(fa[x]);
d[x]=(d[x]+d[fa[x]])%2;
return fa[x]=root;
}
int dp[M][M];
int se[M][3];//2个对立的集合。
int main()
{
int n,p1,p2;
while(scanf("%d%d%d",&n,&p1,&p2))
{
if(n==0&&p1==0&&p2==0)break;
memset(dp,0,sizeof(dp));
memset(se,0,sizeof(se));
map<int,int>ma;
int x,y;
char s[10];
for(int i=1;i<=p1+p2;i++)
fa[i]=i,d[i]=0;
for(int i=1;i<=n;i++)
{
scanf("%d%d%s",&x,&y,s);
int gx=get(x),gy=get(y);
if(s[0]=='y')
{
if(gx!=gy)
{
fa[gx]=gy;
d[gx]=(d[x]+d[y])%2;
}
}
else
{
if(gx!=gy)
{
fa[gx]=gy;
d[gx]=(1+d[x]+d[y])%2;
}
}
}
int cnt=0;
for(int i=1;i<=p1+p2;i++)
{
int gi=get(i);
if(ma[gi]==0)ma[gi]=++cnt;//祖宗编号对应这第个集合 ,方便后面找到路径
se[ma[gi]][d[i]]++;
// if(size[gi]==1)
//se[ma[gi]][!d[i]]++;
}
/* for(int i=1;i<=cnt;i++)
{
printf("s1=%d s2=%d\n",se[i][0],se[i][1]);
}*/
dp[0][0]=1;
for(int i=1;i<=cnt;i++)
{
for(int j=0;j<=p1;j++)
{
if(j>=se[i][0])
dp[i][j]+=dp[i-1][j-se[i][0]];
if(j>=se[i][1])
dp[i][j]+=dp[i-1][j-se[i][1]];
//printf("i=%d j=%d dp[i][j]=%d\n",i,j,dp[i][j]);
}
}
int j=p1;
// printf(" %d +++++\n",dp[cnt][p1]);
if(dp[cnt][p1]==1)
{
memset(judge,-1,sizeof(judge));
for(int i=cnt;i>=1;i--)
{
if(dp[i-1][j-se[i][0]]==dp[i][j])
{
j-=se[i][0];
judge[i]=0;
}
else if(dp[i-1][j-se[i][1]]==dp[i][j])
{
j-=se[i][1];
judge[i]=1;//第i个集合选择1类的人
}
}
for(int i=1;i<=p1+p2;i++)
{
int gi=get(i);
int loc=ma[gi];//i的祖宗对应的集合编号
if(judge[loc]==d[i])//判断是否选择这个集合 以及 要选择的类别与该点是否相同
printf("%d\n",i);//从小到大输出保证了升序
}
puts("end");
}
else
puts("no");
}
return 0;
}