zoukankan      html  css  js  c++  java
  • poj1417(带权并查集+背包DP+路径回溯)

    题目链接:http://poj.org/problem;jsessionid=8C1721AF1C7E94E125535692CDB6216C?id=1417

    题意:有p1个天使,p2个恶魔,天使只说真话,恶魔只说假话。问n句话,问x:y是否为天使,x回答yes或no,分别表示是或否,问能否确认为天使的人的编号(1..p1+p2),若能按顺序1输出,否则输出no。

    思路:

    看到带权并查集的题首先考虑到向量,先分析一下,不访设x->y表示x说y是什么,0表示天使,1表示是恶魔,枚举一下会发现x->y=0也表示x y同类,=1表示x y异类。并且x->z=(x->y)^(y->z)。这样就很清晰了,我们使用并查集将有关系的人并起来,并得到每个人与其祖先的关系,这样之后就会得到一些集合,每个集合有两类人。我想到这了就不知道怎么做了,因为我是在刷bin巨并查集专题看到的这题,我的潜意识就是怎么用并查集去解决这个问题,所以我一直在想是不是有其他的并查集的方法。看了别人的博客才恍然大悟之后就是一个完全背包的题了啊。还是太年轻了,思维应该开阔些,不能局限在一种思维上去想怎么做题。

    回到题目,利用并查集得到这些集合比较简单,稍微熟悉并查集都能想到,接下来的背包DP和路径回溯才是这道题的核心。先遍历一遍用r[i]表示第i个集合的祖先,用a[i][0]表示第i个集合与祖先同类的人数,用a[i][1]表示第i个集合与祖先异类的人数。之后就是dp部分,dp[i][j]表示前i个集合中天使个数为j的方法数,按背包模板来就行。当dp[tot][p1]==1时有解,否则输出“no"。若有解还需要输出编号,只需要从dptot][p1]往前回溯即可,因为要按升序,需要sort一下。至此这道题才算结束,但因为敲错了一个变量名,我找了一个小时bug,写代码时还是要心细,不然太折磨人了。

    代码如下:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 using namespace std;
     5 
     6 int n,p1,p2,x,y,d;
     7 int root[605],f[605],dp[605][305],r[605],a[605][2],res[305];
     8 
     9 int getr(int k){
    10     if(root[k]==k) return k;
    11     else{
    12         int tmp=root[k];
    13         root[k]=getr(root[k]);
    14         f[k]^=f[tmp];
    15         return root[k];
    16     }
    17 }
    18 
    19 int main(){
    20     while(scanf("%d%d%d",&n,&p1,&p2),n||p1||p2){
    21         for(int i=1;i<=p1+p2;++i)
    22             root[i]=i,f[i]=0,a[i][0]=a[i][1]=0;
    23         while(n--){
    24             char ch[5];
    25             scanf("%d%d%s",&x,&y,ch);
    26             if(ch[0]=='y') d=0;
    27             else d=1;
    28             int rx=getr(x),ry=getr(y);
    29             if(rx!=ry){
    30                 root[ry]=rx;
    31                 f[ry]=f[x]^f[y]^d;
    32             }
    33         }
    34         int tot=0;
    35         memset(dp,0,sizeof(dp));
    36         for(int i=1;i<=p1+p2;++i){
    37             if(i==getr(i)){
    38                 r[++tot]=i;
    39                 for(int j=1;j<=p1+p2;++j)
    40                     if(getr(j)==i)
    41                         if(f[j]==0) a[tot][0]++;
    42                         else a[tot][1]++;
    43             }
    44         }
    45         dp[0][0]=1;
    46         for(int i=1;i<=tot;++i){
    47             for(int j=p1;j>=a[i][0];--j)
    48                 dp[i][j]+=dp[i-1][j-a[i][0]];
    49             for(int j=p1;j>=a[i][1];--j)
    50                 dp[i][j]+=dp[i-1][j-a[i][1]];
    51         }
    52         if(dp[tot][p1]!=1){
    53             printf("no
    ");
    54             continue;
    55         }    
    56         int p=p1,num=0;
    57         for(int i=tot;i>=1;--i)
    58             if(dp[i-1][p-a[i][0]]==1){
    59                 for(int j=1;j<=p1+p2;++j)
    60                     if(getr(j)==r[i]&&f[j]==0) 
    61                         res[num++]=j;
    62                 p-=a[i][0];
    63             }
    64             else{
    65                 for(int j=1;j<=p1+p2;++j)
    66                     if(getr(j)==r[i]&&f[j]==1)
    67                         res[num++]=j;
    68                 p-=a[i][1];
    69             }
    70         sort(res,res+num);
    71         for(int i=0;i<num;++i)
    72             printf("%d
    ",res[i]);
    73         printf("end
    ");
    74     }
    75     return 0;
    76 }
  • 相关阅读:
    【做题记录】区间排序—线段树
    【做题记录】CF1428E Carrots for Rabbits—堆的妙用
    线段树合并、分裂
    一、drf入门规范
    七、Django实战--图书管理系统搭建
    六、ORM模型层补充
    五、Django之模型层
    四、Django之模板层
    三、Django之视图层
    二、Django之路由层
  • 原文地址:https://www.cnblogs.com/FrankChen831X/p/10466608.html
Copyright © 2011-2022 走看看