题意:有p1个好人,有p2个坏人,给出n个信息,每个信息表示编号为x的人和编号为y的人是否为同一集合,问是否能唯一确定p1中有哪些人。
思路:首先要做一个并查集,用rt[i]记录i与根节点的关系,0表示与根节点属于同一集合,1表示与根节点所在集合不同,这样把所有信息处理完以后,会得到若干个集合,每个集合中只能获得不同集合的人数,但是不能知道某个集合是否是好人。这样的话还要做一个dp,用dp[i][j]表示前i个集合能组合出j个人数的种数,跟背包差不多的东西,做完以后判断一下dp[k][p1]是否为1(k为集合个数)就行了,由于还要输出好人的编号,因此还要回溯路径,找出某一集合中的元素跟根节点相同的是好人还是不同的是好人就行了,这东西写起来感觉蛮麻烦的,幸好没调多长时间就过了,不然要烦死。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=600+10;
int parents[maxn],rt[maxn];
int con[maxn],cnt[maxn],diff[maxn],ron[maxn];
int ans[maxn];
int dp[maxn][maxn];
int Find(int x)
{
if(x==parents[x]) return x;
int tmp=parents[x];
Find(parents[x]);
rt[x]=rt[tmp]^rt[x];
parents[x]=parents[tmp];
return parents[x];
}
void Uion(int x,int y,int v)
{
int a=Find(x);
int b=Find(y);
if(a!=b)
{
if(v)
{
rt[b]=rt[x]^rt[y]^v;
parents[b]=a;
}
else
{
rt[b]=rt[x]^rt[y];
parents[b]=a;
}
}
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int n,p1,p2;
int x,y;
char str[5];
while(~scanf("%d%d%d",&n,&p1,&p2))
{
if(n==0&&p1==0&&p2==0) break;
int m=p1+p2;
for(int i=1;i<=m;++i) parents[i]=i,rt[i]=0;
memset(con,0,sizeof(con));
memset(cnt,0,sizeof(cnt));
memset(diff,0,sizeof(diff));
memset(dp,0,sizeof(dp));
for(int i=0;i<n;++i)
{
scanf("%d%d%s",&x,&y,str);
if(str[0]=='y') Uion(x,y,0);
else Uion(x,y,1);
}
for(int i=1;i<=m;++i) Find(i);
int k=0,pa;
for(int i=1;i<=m;++i)
{
pa=parents[i];
if(con[pa]==0)
{
con[pa]=++k;
ron[k]=pa;
}
cnt[con[pa]]++;
if(rt[i]) diff[con[pa]]++;
}
dp[0][0]=1;
bool flag=true;
for(int i=1;i<=k;++i)
{
x=diff[i];y=cnt[i]-diff[i];
if(x==y) flag=false;
for(int j=m;j>=x;--j)
dp[i][j]+=dp[i-1][j-x];
for(int j=m;j>=y;--j)
dp[i][j]+=dp[i-1][j-y];
}
if(dp[k][p1]!=1||!flag)
printf("no\n");
else
{
int now=p1;
for(int i=k;i>=1;--i)
{
x=diff[i];y=cnt[i]-diff[i];
if(dp[i-1][now-x]==1)
{
ans[ron[i]]=1;
now-=x;
}
else if(dp[i-1][now-y]==1)
{
ans[ron[i]]=0;
now-=y;
}
}
for(int i=1;i<=m;++i)
{
pa=parents[i];
if(ans[pa]==rt[i])
printf("%d\n",i);
}
printf("end\n");
}
}
return 0;
}