题解思路:
通过题意可以推出如果回答为yes,则a b同族,no为不同族;
并查集维护,可以知道有几棵树;
用cnt[i][1 / 0]表示每棵树里2类人各有多少;
看能否拼出为一个解;
dp[i][j]表示前i棵树,可以拼成j个人的个数,
转移方程 dp[i][j]=dp[i-1][j-cnt[i][0]],dp[i-1][j-cnt[i][1]])注意这里是俩类人力必选一类;
dp倒退回去,记录路径输出;
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<map>
#include<queue>
#include<stack>
#include<set>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
const int maxn=600+50;
using namespace std;
int f[maxn],sum[maxn],d[maxn][301],cnt[maxn][2],vis[maxn],choose[maxn];
// 父 路径 dp 记录每类人的数量 重新标号 选择了0/1
int root(int x)
{
if(f[x]==x) return x;
int rt=root(f[x]);
sum[x]^=sum[f[x]];
return f[x]=rt;
}
void init()
{
mem(sum,0);
mem(d,0);
mem(cnt,0);
mem(vis,0);
mem(choose,0);
}
int main(){
int n,s1,s2,a,b,x,y,v,tot;char s[5];
while(~scanf("%d%d%d",&n,&s1,&s2))
{
if(n+s1+s2==0) break;
tot=0;
init();
for(int i=1;i<=s1+s2;i++) f[i]=i;
for(int i=1;i<=n;i++)
{
scanf("%d%d%s",&a,&b,s);
s[0]=='y'?v=0:v=1;
x=root(a);
y=root(b);
if(x!=y)
{
f[x]=y;
sum[x]=sum[a]^sum[b]^v;
}
}
for(int i=1;i<=s1+s2;i++)//重新标号
{
if(!vis[root(i)]) vis[f[i]]=++tot;
if(sum[i]==0)
cnt[vis[f[i]]][0]++;
else
cnt[vis[f[i]]][1]++;
}
// for(int i=1;i<=tot;i++)
// {
// cout<<cnt[i][0]<<" "<<cnt[i][1]<<endl;
// }cout<<endl;
d[0][0]=1;
for(int i=1;i<=tot;i++)//dp
{
for(int j=0;j<=s1;j++)
{
if(j>=cnt[i][1])
d[i][j]+=d[i-1][j-cnt[i][1]];
if(j>=cnt[i][0])
d[i][j]+=d[i-1][j-cnt[i][0]];
}
}
// for(int i=1;i<=tot;i++)
// {
// for(int j=1;j<=s1;j++)
// cout<<d[i][j]<<" ";
// cout<<endl;
// }
if(d[tot][s1]==1)
{
int l=s1;
for(int i=tot;i>0;i--)//倒推路径
{
if(d[i][l]==d[i-1][l-cnt[i][1]])
{
choose[i]=1;
l-=cnt[i][1];
}
else if(d[i][l]==d[i-1][l-cnt[i][0]])
{
choose[i]=0;
l-=cnt[i][0];
}
}
for(int i=1;i<=s1+s2;i++)
{
int x=vis[f[i]];
int y=sum[i];
if(choose[x]==y)
{
printf("%d
",i);
}
}
printf("end
");
}
else printf("no
");
}
return 0;
}