题目链接:POJ-1417
主要思路:
首先你得明白,好人眼中的好人是好人,坏人眼中的好人是坏人,坏人眼中的坏人是好人一个人眼中的好人是和他一样的人。故用拓展域确定其关系,再用DP枚举每一个集合内(一个集合指两个队里的团体)哪个团体是好人。若不可以唯一确定则输出-1.
AC代码:
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define M 1205
using namespace std;
struct node{
int same,dif;
}S[M];
char s[10];
int fa[M],cnt[M];
int getfa(int v){
if(fa[v]==v)return fa[v];
return fa[v]=getfa(fa[v]);
}
vector<int>ANS,T;
int dp[M][305];
bool pre[M][305];
int main(){
int n,p,q;
while(~scanf("%d%d%d",&n,&p,&q)){
if(!n&&!p&&!q)return 0;
int N=p+q;
memset(cnt,0,sizeof(cnt));
for(int i=1;i<=N*2;i++)fa[i]=i;//拓展域
while(n--){
int a,b;
scanf("%d%d%s",&a,&b,s);
int X1=getfa(a),Y1=getfa(b),X2=getfa(a+N),Y2=getfa(b+N);
if(s[0]=='n')fa[X1]=Y2,fa[X2]=Y1;//若两个人不是同一身份
else fa[X1]=Y1,fa[X2]=Y2;//是同一身份
}
int tot=0;
for(int i=1;i<=N;i++){
int x=getfa(i);
if(cnt[x]==0&&x<=N){
S[++tot]=(node){x,getfa(i+N)};//统计此集合内两个团体的人数
}
cnt[x]++;
}
memset(dp,0,sizeof(dp));
dp[0][0]=1;
for(int i=1;i<=tot;i++){//DP过程
for(int j=0;j<=p;j++){
if(dp[i-1][j]){
if(j+cnt[S[i].same]<=p){
dp[i][j+cnt[S[i].same]]+=dp[i-1][j];
pre[i][j+cnt[S[i].same]]=1;
}
if(j+cnt[S[i].dif]<=p){
dp[i][j+cnt[S[i].dif]]+=dp[i-1][j];
pre[i][j+cnt[S[i].dif]]=0;//到这个状态上一个选的是哪个
}
}
}
}
if(dp[tot][p]!=1){//若不唯一确定或不存在
puts("no");
continue;
}
T.clear();
int C=p;
for(int i=tot;i>=1;i--){
if(pre[i][C]){
C-=cnt[S[i].same];
T.push_back(S[i].same);
}else C-=cnt[S[i].dif],T.push_back(S[i].dif);//一个一个往前
}
for(int i=1;i<=N;i++){
int x=getfa(i);
if(find(T.begin(),T.end(),x)!=T.end())ANS.push_back(i);
}
for(int i=0;i<ANS.size();i++)printf("%d\n",ANS[i]);
puts("end");
ANS.clear();
}
}