poj1417 True Liars[并查集+背包]

有一点小转化的题,在设计dp状态时还是有点费脑筋的。

地址


依题意,首先可以知道肯定要扩展域的并查集(明摆着的嘛)。一个"好人"域,一个"坏人"域,每句话分两种情况考虑连边。假设是yes,同域连边,否则异域连边(经典模型嘛)。然后就是要考虑如何验证是否有$x$个好人$y$个坏人的唯一解存在。这取决于联通块。

可以参考我瞎画的图,上面点1~N,下面点N+1~2N。

由于并查集合并时操作的对称性,可以发现一个联通块要么$x$个好人$y$个坏人要么$y$个好人$x$个坏人。那么对于所有联通块必须选其中一种方案,最后要凑齐。于是我就想到二维的背包。。但是复杂度太大了啊。。卡了好久,于是又手玩了样例。发现当我所有联通块恰好凑出x个好人时,剩下的不就全是坏人吗。所以只要去做一个好人的背包就行了。dp的时候由于没处理好关于多解的问题,又调了半小时。。一题做两个小时我也是醉了。。其实就是有前面一个状态转移,记一下$pre$。在记一下方案。最终好人的背包装满的状态方案不是1种就是无解,是一种就把所有好人找出来,这个我开了vector存了每个联通块。细节还看code,虽然可能写繁掉了qwq。。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 #include<queue>
 7 #include<vector>
 8 #define dbg(x) cerr<<#x<<" = "<<x<<endl
 9 #define ddbg(x,y) cerr<<#x<<" = "<<x<<"   "<<#y<<" = "<<y<<endl
10 using namespace std;
11 typedef long long ll;
12 template<typename T>inline char MIN(T&A,T B){return A>B?A=B,1:0;}
13 template<typename T>inline char MAX(T&A,T B){return A<B?A=B,1:0;}
14 template<typename T>inline T _min(T A,T B){return A<B?A:B;}
15 template<typename T>inline T _max(T A,T B){return A>B?A:B;}
16 template<typename T>inline T read(T&x){
17     x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1;
18     while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x;
19 }
20 const int N=600+7;
21 int fa[N<<1],f[N][N>>1],cnt[N][N>>1],pos[N],tot,tot2,ans[N];
22 vector<int> a[N],b[N];
23 int n,m,gd,bd,x,y;
24 inline int Get(int x){return fa[x]^x?fa[x]=Get(fa[x]):x;}
25 
26 int main(){//freopen("test.in","r",stdin);//freopen("test.out","w",stdout);
27     while(read(m),read(gd),read(bd),m||gd||bd){
28         n=gd+bd;char s[5];tot=0,tot2=0;memset(pos,0,sizeof pos);
29         for(register int i=1;i<=n;++i)a[i].clear(),b[i].clear();
30         for(register int i=1;i<=(n<<1);++i)fa[i]=i;
31         for(register int i=1;i<=m;++i){
32             read(x),read(y),scanf("%s",s);
33             if(x==y)continue;
34             if(s[0]=='y')fa[Get(x)]=Get(y),fa[Get(x+n)]=Get(y+n);
35             else fa[Get(x)]=Get(y+n),fa[Get(x+n)]=Get(y);
36         }
37         for(register int i=1;i<=n;++i)if(Get(i)<=n)a[Get(i)].push_back(i);
38         for(register int i=n+1;i<=(n<<1);++i)if(Get(i)<=n)b[Get(i)].push_back(i-n);
39         for(register int i=1;i<=n;++i)if(!a[i].empty()||!b[i].empty())pos[++tot]=i;//ÁªÍ¨¿éͳ¼Æ 
40         memset(f,0,sizeof f);memset(cnt,0,sizeof cnt);cnt[0][0]=1;
41         for(register int i=1;i<=tot;++i){
42             x=a[pos[i]].size(),y=b[pos[i]].size();
43             for(register int j=0,lx=j-x,ly=j-y;j<=gd;++j,lx=j-x,ly=j-y){
44                 if(lx<0&&ly>=0)f[i][j]=ly,cnt[i][j]=cnt[i-1][ly];
45                 else if(lx>=0&&ly<0)f[i][j]=lx,cnt[i][j]=cnt[i-1][lx];
46                 else if(lx>=0&&ly>=0)f[i][j]=cnt[i-1][lx]?lx:ly,cnt[i][j]=cnt[i-1][lx]+cnt[i-1][ly];
47             }
48         }//×ödp 
49         if(cnt[tot][gd]^1)printf("no\n");
50         else{
51             int j=gd;
52             while(tot){
53                 if(j-f[tot][j]==a[pos[tot]].size()){for(register int i=0;i<a[pos[tot]].size();++i)ans[++tot2]=a[pos[tot]][i];}
54                 else for(register int i=0;i<b[pos[tot]].size();++i)ans[++tot2]=b[pos[tot]][i];
55                 j=f[tot--][j];
56             }//ÍÆ»ØÈ¥ 
57             sort(ans+1,ans+tot2+1);
58             for(register int i=1;i<=tot2;++i)printf("%d\n",ans[i]);
59             printf("end\n");
60         }
61     }
62     return 0;
63 }

猜你喜欢

转载自www.cnblogs.com/saigyouji-yuyuko/p/10639594.html