[POJ 1417]true liars

题目传送-POJ1417

题意:

一共有\(p1\)个好人,\(p2\)个坏人.
现在有\(m\)句话(a,b,YES/NO)
表示\(a\)\(b\)是否是好人
好人都说真话,坏人都说假话
问方案是否唯一,并输出唯一的方案
\(p1,p2\le300,m\le1000\)

题解:

观察性质发现:当为NO时,ab好坏相反,否则相同
那么考虑并查集维护出敌对和友好关系
其中每一个团有一个敌对团,即两个团之间的好坏必然相反
那么把这些敌对的数量搞出来,背包一发就行了
路径输出不解释了

过程:

并查集记得把关系都建满了..

代码:

const int N=1210;
int p1,p2,n,m;
int fat[N<<1],sz[N<<1];
char s[20];
int father(int x) {return fat[x]==x ? x : fat[x]=father(fat[x]);}
inline void Union(int x,int y) {
	int fx=father(x),fy=father(y);
	if(fx!=fy) {fat[fx]=fy; sz[fy]+=sz[fx]; sz[fx]=0;}
}
int used[N];
pii a[N][2]; int tot=0;
int f[N][N],fr[N][N];
int cho[N],ind=0;
void Print(int dep,int j) {
	if(dep==0) return;
	int type=fr[dep][j];
	cho[++ind]=a[dep][type].S;
	Print(dep-1,j-a[dep][type].F);
}
int ans[N],res=0;
signed main() {
	while(scanf("%d %d %d",&m,&p1,&p2) && (m || p1 || p2)) {
		ind=tot=res=0; mem(used,0); bool fl=1;
		n=p1+p2; mem(sz,0); mem(f,0);
		for(int i=1;i<=n*2;i++) fat[i]=i;
		for(int i=1;i<=n;i++) sz[i]=1;
		for(int i=1;i<=m;i++) {
			int x,y; read(x); read(y); scanf("%s",s+1);
			Union(x+(s[1]=='y' ? 0 : n),y);
			Union(x,y+(s[1]=='y' ? 0 : n));
			if(s[1]=='y') Union(x+n,y+n);
		}
		for(int i=n+1;i<=2*n;i++) {
			int fx=father(i),fy=father(i-n);
			// printf("%d %d\n",fx,fy);
			if(fx==fy) {puts("no"); fl=0; break;}
			if(used[fx]) {assert(used[fy]); continue;}
			used[fx]=used[fy]=1;
			a[++tot][0]=mp(sz[fx],fx);
			a[tot][1]=mp(sz[fy],fy);
		}
		if(!fl) continue;
		f[0][0]=1;
		// printf("tot=%d\n",tot);
		// for(int i=1;i<=tot;i++) printf("%d %d\n",a[i][0].F,a[i][1].F);
		for(int i=1;i<=tot;i++) {
			int bd=a[i][0].F;
			for(int j=p1;j>=bd;j--)
				if(f[i-1][j-bd]) {
					fr[i][j]=0;
					f[i][j]+=f[i-1][j-bd];
					if(f[i][j]>1) f[i][j]=2;
				}
			bd=a[i][1].F;
			for(int j=p1;j>=bd;j--)
				if(f[i-1][j-bd]) {
					fr[i][j]=1;
					f[i][j]+=f[i-1][j-bd];
					if(f[i][j]>1) f[i][j]=2;
				}
		}
		if(f[tot][p1]==1) {
			Print(tot,p1);
			for(int i=1;i<=n;i++) {
				int anc=father(i);
				for(int j=1;j<=ind;j++)
					if(anc==cho[j]) {ans[++res]=i; break;}
			}
			sort(ans+1,ans+res+1);
			for(int i=1;i<=res;i++)
				printf("%d\n",ans[i]);
			puts("end");
		} else puts("no");
	}
	return 0;
}

用时:1h

posted @ 2018-08-18 18:27  functionendless  阅读(211)  评论(0编辑  收藏  举报