zoukankan      html  css  js  c++  java
  • 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 }
  • 相关阅读:
    随机生成30道四则运算题目
    《构建之法》阅读笔记01
    第一周学习进度
    个人介绍
    MyBatis(登录)
    MyBatis
    动态网页
    网页基本标签
    Servlet基础
    JSP数据交互
  • 原文地址:https://www.cnblogs.com/cnblogs321114287/p/4491764.html
Copyright © 2011-2022 走看看