zoukankan      html  css  js  c++  java
  • 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
    ");
     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
    ",ans[i]);
    107             printf("end
    ");
    108         }
    109     }
    110     return 0;
    111 }
  • 相关阅读:
    字符串的操作
    10.20 整理1
    if else; while; break;continue ----流程控制系列
    10.19 重新打了第一天的代码(课件))
    10.17
    svn,导入数据到版本库及使用工作副本
    mysql的反引号backtick
    css中的1px并不等于设备的1px
    onclick事件属性与在用js绑定onclick事件中的this的区别
    一次对CI框架update方法底层实现的探索之旅
  • 原文地址:https://www.cnblogs.com/ZQUACM-875180305/p/9103525.html
Copyright © 2011-2022 走看看