True Liars POJ - 1417 并查集拓展域+DP

题目链接: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();
	}
}

猜你喜欢

转载自blog.csdn.net/qq_35320178/article/details/81811198