题目大意:
一共有p1+p2个人,分成两组,一组p1,一组p2。给出N个条件,格式如下:
x y yes表示x和y分到同一组
x y no表示x和y分到不同组
问分组情况是否唯一,若唯一则输出方案,否则输出no。保证不存在矛盾条件,但是有可能出现x=y的情况。
参考资料:http://hi.baidu.com/buaa_babt/blog/item/ff6c6c40ba89a2136a63e53a.html
CODE:
/*并查集+背包*/
/*注意判断唯一的时候的DP方案对于求最终的解会有偏差,因为有可能使之前确定下来的方案被新方案覆盖*/
/*AC代码:63ms*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <memory.h>
#include <cstring>
#define MAXN 605
using namespace std;
int M,P1,P2,N,scc;
int p[MAXN];//每组集合的根节点
int rel[MAXN];//每个点与根节点的关系0:相同(yes);1:不同(no)
int num[MAXN][2];//num[i][j]表示某个根节点i下j组的点数
int root[MAXN];
int dp[MAXN][MAXN];//dp[i][j]表示到达说真话有i人,说假话有j人的情况种数
int pre[MAXN][MAXN];//记录路径
bool ans[MAXN];
int find_set(int x)
{
if(p[x]!=x)
{
int t=find_set(p[x]);
rel[x]^=rel[p[x]];
p[x]=t;
}
return p[x];
}
void Init()
{
int i,j,u,v,w;
char s[10];
N=P1+P2;
memset(num,0,sizeof(num));
//初始化
for(i=1;i<=N;i++)
{
p[i]=i;
rel[i]=0;
}
for(i=1;i<=M;i++)
{
scanf("%d%d%s",&u,&v,s);
w=(s[0]=='n');
int du=find_set(u);
int dv=find_set(v);
if(du!=dv)
{
p[du]=dv;//每当根节点改变时他和根节点的关系也要更新
rel[du]=rel[u]^rel[v]^w;
}
}
//注意点
for(i=1;i<=N;i++)
p[i]=find_set(i);
scc=0;
for(i=1;i<=N;i++)
{
if(p[i]==i)
{
scc++;
root[scc]=i;
for(j=1;j<=N;j++)
{if(p[j]==i) ++num[scc][rel[j]];}
}
}
}
void Solve()
{
int i,j,k,x,y;
memset(dp,0,sizeof(dp));
dp[0][0]=1;
for(i=1;i<=scc;i++)
{
for(j=P1;j>=0;j--)
for(k=P2;k>=0;k--)
{
x=j-num[i][0];y=k-num[i][1];
if(x>=0&&y>=0&&dp[x][y])
dp[j][k]+=dp[x][y];
x=j-num[i][1];y=k-num[i][0];
if(x>=0&&y>=0&&dp[x][y])
dp[j][k]+=dp[x][y];
}
}
if(dp[P1][P2]!=1) printf("no\n");
else
{
memset(pre,0,sizeof(pre));
memset(dp,0,sizeof(dp));
dp[0][0]=1;
for(i=1;i<=scc;i++)
{
for(j=P1;j>=0;j--)
for(k=P2;k>=0;k--)
{
if(dp[j][k]) continue;
x=j-num[i][0];y=k-num[i][1];
if(x>=0&&y>=0&&dp[x][y])
{
dp[j][k]+=dp[x][y];
pre[j][k]=0;
continue;
}
x=j-num[i][1];y=k-num[i][0];
if(x>=0&&y>=0&&dp[x][y])
{
dp[j][k]+=dp[x][y];
pre[j][k]=1;
continue;
}
}
}
x=P1;y=P2;
memset(ans,false,sizeof(ans));
for(i=scc;i>=1;i--)
{
for(j=1;j<=N;j++)
{if(root[i]==p[j]&&rel[j]==pre[x][y]) ans[j]=true;}
if(pre[x][y])
{x-=num[i][1];y-=num[i][0];}
else
{x-=num[i][0];y-=num[i][1];}
}
for(i=1;i<=N;i++)
{
if(ans[i])
printf("%d\n",i);
}
printf("end\n");
}
}
int main()
{
while(scanf("%d%d%d",&M,&P1,&P2)!=EOF)
{
if(M==0&&P1==0&&P2==0) break;
Init();
Solve();
}
return 0;
}