C - BLG POJ - 1417 种类并查集加dp(背包)

思路:刚看这道题感觉什么都不清楚,人物之间的关系一点也看不出来,都不知道怎么写,连并查集都没看出来,但是你可以仔细分析一下,当输入字符串为“yes”的时候,我们设输入的值为x和y,当x为天使是则由题可知y也为天使;当x为魔鬼的时候,则y也为魔鬼,所以输入“yes”的时候就相当于说他们是同类。

当输入字符串为“no”的时候,如果x为天使,则y为魔鬼;x为魔鬼的时候,y就是天使,所以当输入字符串为“no”的时候他们为异类。。这不就是种类并查集的

0 (同类) 1(异类)并查集嘛!

再接着想,通过并查集我们可以把它们分成两类,一种是和他们的根节点同类,一种不是同类。。这一点要注意了,所有的他们并不是一定是一个父节点,假如

(1  2)(3  4) 那他们就不在一棵树上。。

用完并查集我们就要用一个数组来记录他们根节点的数目,而且还要记录每一个根节点与其同类和异类的两种情况的数目,然后我们只要用这些数可以组合出来

天使或魔鬼的数目就可以了,但是要保证只有一种方法可以,如果有好几种方法,那就无法确定。。还要注意的是当去组成天使或魔鬼的数目的时候,一个根节点只能选根节点的同类或异类来往上加,不能都选。。。

上一点是不是有点像01背包,用一些数来组成一个数,看有几种方法

最后就是输出路径(路径方法我之前写过,你也可以在网上搜一下)

接着就上代码吧!

  1 //https://www.cnblogs.com/geloutingyu/p/6142473.html    我是看这个网址才懂得,可以看一下
  2 #include<stdio.h>
  3 #include<string.h>
  4 #include<iostream>
  5 #include<algorithm>
  6 using namespace std;
  7 int v[505],w[505],n,a,b,gg[505],jj[505][505],tag[505][2],dp[505][505],cc[505][2];
  8 struct shudui
  9 {
 10     int start,last;
 11     char q;
 12 }m[2005];
 13 int finds(int x)
 14 {
 15     if(x!=v[x])
 16     {
 17         int y=finds(v[x]);
 18         w[x]=(w[x]+w[v[x]])%2;
 19         v[x]=y;
 20         return y;
 21     }
 22     return x;
 23 }
 24 void join(int x,int y,int z)
 25 {
 26     int fx=finds(x);
 27     int fy=finds(y);
 28     if(fx==fy)
 29     {
 30         return;
 31     }
 32     else
 33     {
 34         v[fx]=fy;
 35         w[fx]=(2-w[x]+z-1+w[y])%2;
 36     }
 37 }
 38 int main()
 39 {
 40     while(~scanf("%d%d%d",&n,&a,&b))
 41     {
 42         memset(w,0,sizeof(w));
 43         if(n+a+b==0) break;
 44         for(int i=1;i<=a+b;++i)
 45         {
 46             v[i]=i;
 47         }
 48         while(n--)
 49         {
 50             char q[5];
 51             int x,y;
 52             scanf("%d%d%s",&x,&y,q);
 53             if(x==y)
 54                 continue;
 55             if(strcmp("no",q)==0)
 56             {
 57                 join(x,y,2);
 58             }
 59             else join(x,y,1);
 60         }
 61         //int w1[1005],w2[1005];
 62         memset(gg,0,sizeof(gg));    //存根节点
 63         memset(jj,0,sizeof(jj));
 64         memset(tag,0,sizeof(tag));  //来记录根节点同来和异类的数目
 65         memset(dp,0,sizeof(dp));    //背包dp,看看有几种方法
 66         memset(cc,0,sizeof(cc));    //标记,做最后输出
 67         int cnt=0;
 68         for(int i=1;i<=a+b;++i) //统计集合个数
 69         {
 70             if(finds(i)==i)
 71             {
 72                 gg[i]=++cnt;
 73             }
 74         }
 75         for(int i=1;i<=a+b;++i)
 76         {
 77             tag[gg[finds(i)]][w[i]]++;
 78         }
 79         dp[0][0]=1;
 80         for(int i=1;i<=cnt;++i)
 81         {
 82             for(int j=0;j<=a;++j)//dp[i][j]表示到第i个集合,人数达到j的方法数
 83             {
 84                 if(j-tag[i][0]>=0 && dp[i-1][j-tag[i][0]])
 85                 {
 86                     dp[i][j]+=dp[i-1][j-tag[i][0]];
 87                     jj[i][j]=tag[i][0];  //jj是记录路径的
 88                 }
 89                 if(j-tag[i][1]>=0 && dp[i-1][j-tag[i][1]])
 90                 {
 91                     dp[i][j]+=dp[i-1][j-tag[i][1]];
 92                     jj[i][j]=tag[i][1];
 93                 }
 94             }
 95         }
 96         if(dp[cnt][a]!=1)
 97         {
 98             printf("no\n");
 99         }
100         else
101         {
102             for(int i=cnt,j=a;j>0 && i>0; i--)
103             {
104                 if(jj[i][j]==tag[i][0])
105                     cc[i][0]=1;
106                 else cc[i][1]=1;
107                 j-=jj[i][j];
108             }
109             for(int i=1;i<=a+b;++i)
110             {
111                 if(cc[gg[finds(i)]][w[i]]) printf("%d\n",i);
112             }
113             printf("end\n");
114         }
115     }
116     return 0;
117 }
View Code

猜你喜欢

转载自www.cnblogs.com/kongbursi-2292702937/p/10693368.html