题目传送-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