poj 1417(并查集与dp的应用)

前置题目:poj1611、poj1182、hdoj3038

有了前面的积累,这道题目自然就想出了运用并查集将它们归为一类。

以及它们之间的关系,主要是在路径压缩的时候发生了改变。这些在poj1182、hdoj3038都可以体会到这一点。

问题的关键是这个测谎仪,要怎么判断对错。

这里看到网上的题解都是采用dp的方法。

dp[i][j]这里i表示前i个集合,好人为j个的方案有几种。

这里我们应该清楚,如果好人的方案超过了一种。就是说,坏人与好人的说法有各所其词的时刻。这时候我们就无法判断,这么多集合方案下面的是否正确。所以无法判断就是假。

 

感觉没有说明白啊。。。。。。没关系,去题目那里可以体会到。不过,写下这篇东西后,感觉思路清晰了很多。orz

  1 #include<stdio.h>
  2 #include<cstring>
  3 #include<vector>
  4 #include<algorithm>
  5 using namespace std;
  6 const int maxn=606;
  7 int fa[maxn],r[maxn];
  8 int p1,p2,p,cnt;
  9 bool vis[maxn];
 10 int dp[maxn][maxn/2],a[maxn][2];
 11 vector<int> b[maxn][2];
 12 
 13 int find_fa(int x) ///路径压缩与查找
 14 {
 15     if(fa[x]==x) return fa[x];
 16     int tmp=fa[x];
 17     fa[x]=find_fa(tmp);
 18     r[x]=(r[x]+r[tmp])%2;
 19     return fa[x];
 20 }
 21 
 22 void init() ///初始化
 23 {
 24     for(int i=0;i<=p1+p2;i++){
 25         fa[i]=i; r[i]=0;
 26     }
 27     cnt=1;
 28     memset( vis, false, sizeof vis);
 29     memset( a, 0, sizeof a);
 30     memset( dp, 0, sizeof dp);
 31     dp[0][0]=1;
 32     for(int i=0;i<maxn;i++){
 33         b[i][0].clear();
 34         b[i][1].clear();
 35     }
 36 }
 37 
 38 int main()
 39 {
 40     while( ~scanf("%d%d%d",&p,&p1,&p2)&&p+p1+p2){
 41         init();
 42         while(p--){
 43             int u,v;
 44             char str[10];
 45             scanf("%d%d%s",&u,&v,str);
 46             int k=(str[0]=='n');
 47             int ra=find_fa(u);
 48             int rb=find_fa(v);
 49             if(ra!=rb){
 50                fa[ra]=rb;
 51                 r[ra]=(2-r[u]+r[v]+k)%2;
 52             }
 53         }
 54 
 55         for(int i=1;i<=p1+p2;i++){
 56             if(!vis[i]){
 57                 int tmp=find_fa(i);
 58                 for(int j=i;j<=p1+p2;j++){
 59                     if(tmp==find_fa(j)){
 60                         vis[j]=true;
 61                         b[cnt][r[j]].push_back(j);
 62                         a[cnt][r[j]]++;
 63                         ///可以清楚地看到这里的a是记录改集合的个数,b记录改集合的元素
 64                     }
 65                 }
 66                 cnt++;
 67             }
 68         }
 69 
 70         for(int i=1;i<cnt;i++){
 71             for(int j=p1;j>=0;j--){ ///p1开始很容易看到
 72                 if(j-a[i][0]>=0)
 73                     dp[i][j]+=dp[i-1][j-a[i][0]];
 74 
 75                 if(j-a[i][1]>=0)
 76                     dp[i][j]+=dp[i-1][j-a[i][1]];
 77                 ///i集合,j个好人的情况。背包的dp
 78             }
 79         }
 80 
 81         if(dp[cnt-1][p1]!=1){ ///说法有很多种,测谎仪失效啦
 82             printf("no\n");
 83         }
 84         else{
 85             vector<int>ans;
 86             ans.clear();
 87             for(int i=cnt-1;i>=1;i--){
 88                 if(p1-a[i][0]>=0&&p2-a[i][1]>=0&&dp[i-1][p1-a[i][0]]==1){
 89                     ///判断中可以看出,我们要找说法唯一的情况
 90                     for(int j=0;j<b[i][0].size();j++){
 91                         ans.push_back(b[i][0][j]);
 92                     }
 93                     p1-=a[i][0];
 94                     p2-=a[i][1];
 95                 }
 96                 else if(p1-a[i][1]>=0&&p2-a[i][0]>=0&&dp[i-1][p1-a[i][1]]==1){
 97                     for(int j=0;j<b[i][1].size();j++){
 98                         ans.push_back(b[i][1][j]);
 99                     }
100                     p1-=a[i][1];
101                     p2-=a[i][0];
102                 }
103             }
104             sort( ans.begin(),ans.end()); ///输出答案
105             for(int i=0;i<ans.size();i++)
106                 printf("%d\n",ans[i]);
107             printf("end\n");
108         }
109     }
110     return 0;
111 }

 

posted @ 2018-05-29 09:48  flyer_duck  阅读(180)  评论(0编辑  收藏  举报