poj 1417 并查集+dp

转自:点我

题目:给出p1+p2个人,其中p1个是好人,p2个是坏人。然后有一些关系 ,a说b是好人(坏人).其中没有矛盾的,判断是否有唯一解判断哪些人是好人,哪些人是坏人。

其中比较重要的是,好人总说真话,坏人总说假话。不需要判断矛盾。唯一解

http://poj.org/problem?id=1417 

其中好人说真话,坏人说假话这点很重要。

那么如果一个人说另一个人是好人,那么如果这个人是好人,说明 对方确实是好人,如果这个是坏人,说明这句话是假的,对方也是坏人。

如果一个人说另一个人是坏人,那么如果这个人是好人,说明对方是坏人,如果这个是坏人,说明 对方是好人。

也就是如果条件是yes说明这两个是相同集合的,否则是两个不同的集合。

用r[i]表示i结点与根结点的关系,0为相同集合,1为不同集合。这是一个经典的并查集问题。

这样处理之后,还需要判断是否唯一

我们通过并查集,可以将所有人分为若干个集合,其中对于每一个集合,又分为两个集合(好人和坏人,但是不知道哪些是好人,哪些是坏人,我们只有相对关系)

接下来就是从所有大集合中的两个小集合取一个,组成好人集合,判断是否唯一。

背包问题,dp[i][j]表示前i个大集合,好人为j个的方案有多少种,或者dp[i][j]表示当前好人i个,坏人j个的情况有多少种

如果dp[cnt][p1]!=1说明方案不唯一,或者无解。

如果为1题目还需要输出方案,这点比较纠结。用后一种DP的时候WA了好多次,而这题又卡内存,不能开三维数组,其实可以两次DP解决。

后来采用前者DP,不断从dp[cnt][p1]往前递推,递推的结果也必须是某个前趋状态的dp值为1.

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<map>
  4 #include<cstring>
  5 #include<cmath>
  6 #include<vector>
  7 #include<algorithm>
  8 #include<set>
  9 #include<string>
 10 #include<queue>
 11 #define inf 1<<30
 12 #define M 60005
 13 #define N 605
 14 #define maxn 300005
 15 #define pb(a) push_back(a)
 16 #define mem(a,b) memset(a,b,sizeof(a))
 17 using namespace std;
 18 int pre[N],r[N];
 19 int p1,p2,p;
 20 bool vis[N];
 21 int dp[N][N/2];
 22 int cnt;   //最后分为几个集合
 23 int a[N][2];  //a[i][0],a[i][1]分别表示把第i个集合分成的两个部分
 24 vector<int> b[N][2];
 25 int find(int x)
 26 {
 27     if(x!=pre[x])
 28     {
 29         int f=pre[x];
 30         pre[x]=find(pre[x]);
 31         r[x]=r[x]^r[f];
 32     }
 33     return pre[x];
 34 }
 35 void Init()
 36 {
 37     for(int i=1; i<=p1+p2; i++) pre[i]=i,r[i]=0;
 38     mem(vis,false);
 39     cnt=1;
 40     mem(a,0);
 41     for(int i=0; i<N; i++)
 42     {
 43         b[i][0].clear();
 44         b[i][1].clear();
 45     }
 46 }
 47 int main()
 48 {
 49     while(scanf("%d%d%d",&p,&p1,&p2)!=EOF&&p+p1+p2)
 50     {
 51         Init();
 52         while(p--)
 53         {
 54             int u,v;
 55             char str[10];
 56             scanf("%d%d%s",&u,&v,str);
 57             int k=(str[0]=='n');
 58             int ra=find(u),rb=find(v);
 59             if(ra!=rb)
 60             {
 61                 pre[ra]=rb;
 62                 r[ra]=r[u]^r[v]^k;
 63             }
 64         }
 65         for(int i=1; i<=p1+p2; i++)
 66         {
 67             if(!vis[i])
 68             {
 69                 int f=find(i);
 70                 for(int j=i; j<=p1+p2; j++)
 71                 {
 72                     if(find(j)==f)
 73                     {
 74                         vis[j]=true;
 75                         b[cnt][r[j]].pb(j);
 76                         a[cnt][r[j]]++;
 77                     }
 78                 }
 79                 cnt++;
 80             }
 81         }
 82         mem(dp,0);
 83         dp[0][0]=1;
 84         for(int i=1; i<cnt; i++)
 85         {
 86             for(int j=p1; j>=0; j--)
 87             {
 88                 if(j-a[i][0]>=0)
 89                     dp[i][j]+=dp[i-1][j-a[i][0]];
 90                 if(j-a[i][1]>=0)
 91                     dp[i][j]+=dp[i-1][j-a[i][1]];
 92             }
 93         }
 94         if(dp[cnt-1][p1]!=1)
 95         {
 96             printf("no
");
 97             continue;
 98         }
 99         else
100         {
101             vector<int>ans;
102             ans.clear();
103             for(int i=cnt-1; i>=1; i--)
104             {
105                 if(p1-a[i][0]>=0&&p2-a[i][1]>=0&&dp[i-1][p1-a[i][0]]==1)
106                 {
107                     for(int j=0; j<b[i][0].size(); j++)
108                     {
109                         ans.pb(b[i][0][j]);
110                     }
111                     p1-=a[i][0];
112                     p2-=a[i][1];
113                 }
114                 else if(p1-a[i][1]>=0&&p2-a[i][0]>=0&&dp[i-1][p1-a[i][1]]==1)
115                 {
116                     for(int j=0; j<b[i][1].size(); j++)
117                     {
118                         ans.pb(b[i][1][j]);
119                     }
120                     p1-=a[i][1];
121                     p2-=a[i][0];
122                 }
123             }
124             sort(ans.begin(),ans.end());
125             for(int i=0; i<ans.size(); i++) printf("%d
",ans[i]);
126             printf("end
");
127         }
128     }
129     return 0;
130 }
原文地址:https://www.cnblogs.com/cnblogs321114287/p/4491764.html