[POJ-1417]种类 并查集 + 背包 + 找路径

http://poj.org/problem?id=1417 题目链接这里

又是一题种类并查集,因为poj崩掉了,我就当ac了;
以下是代码;

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>

using namespace std;

int f[1010], relation[1010], type1[1010], type2[1010]; // relation是种类并查集关系,
//type1 纪录与根节点的同类,type2纪录另一类
int dp[1010][1010];
int parent[1010]; //存根节点
int vis[1010]; 
int ans[1010];  
int Find(int x)
{
     if(f[x] == x)
     return x;
     int tmp = f[x];
     f[x] = Find(f[x]);
     relation[x] = relation[x] ^ relation[tmp]; //种类并查集最神奇的地方就是这个关系,
     //参考了大佬的代码才写出来,比较玄学,这种两个种类的应该就是这个关系写法
     return f[x];
}

void Union(int x, int y, int rela)
{
   int rx = Find(x);
   int ry = Find(y);
   if(rx != ry)
   {
       f[rx] = ry;
       relation[rx] = relation[x]^relation[y]^rela;//合并根节点,注意关系域的转移
   }
}

int main()
{
     int n, p1, p2;
     while(scanf("%d%d%d", &n, &p1, &p2) && (p1 + p2 + n))
     {
         for(int i = 1; i <= p1 + p2; i++)
         {
            f[i] = i, relation[i] = 0, type1[i] = 0, type2[i] = 0;
            vis[i] = 0;
         }
         memset(dp, 0, sizeof(dp));
         for(int i = 0; i < n; i++)
         {
             int a, b;
             char s[8];
             scanf("%d%d%s", &a, &b, s);
             if(s[0] == 'y') Union(a, b, 0);//同类会说yes
             else Union(a, b, 1);  
         }
         int cnt = 1;
         for(int i = 1; i <= p1 + p2; i++)
         {
             if(!vis[i])
             {
                 int fa = Find(i);
                 for(int j = i; j <= p1 + p2; j++)
                 {
                     if(Find(j) == fa && !vis[j])
                     {
                         vis[j] = 1;
                         if(relation[j] == 0) type1[cnt]++;
                         else type2[cnt]++;  // 找出根结点并且纪录两个种类的数目
                     }
                 }
                     parent[cnt++] = fa;
             }
         }
         dp[0][0] = 1; //方案数背包,看看是不是只有一种方案能满足凑出p1来,凑p2也可以
         for(int i = 1; i < cnt; i++)
         {
              int Min = min(type1[i], type2[i]);
              for(int j = p1; j >= Min; j--)
              {
                  if(dp[i - 1][j - type1[i]])
                  {
                     dp[i][j] += dp[i - 1][j - type1[i]];
                   }
                   if(dp[i - 1][j - type2[i]])
                   {
                      dp[i][j] += dp[i - 1][j - type2[i]];
                   }
               //printf("%d %d %d\n",i, j, dp[i][j]);
               }
         }
        // printf("%d\n", dp[cnt-1][p1]);
        // printf("cnt %d", cnt);
         if(dp[cnt-1][p1] != 1)
         {
            printf("no\n");
            continue;
         }
         int num = 0;
         int lost = p1;  // 路径回溯找到节点存起来
         for(int i = cnt - 1; i >= 1; i--)
         {
             if(dp[i - 1][lost - type1[i]] == 1)
             {
                  for(int j = 1; j <= p1 + p2; j++)
                  {
                     if(Find(j) == parent[i] && relation[j] == 0)
                     ans[num++] = j;
                  }
                  lost -= type1[i];
             }
             else
             {
                  for(int j = 1; j <= p1 + p2; j++)
                  if(Find(j) == parent[i] && relation[j] == 1)
                  ans[num++] = j;
                  lost -= type2[i];
             }
         }
         sort(ans, ans + num);
         for(int i = 0; i < num; i++)
         printf("%d\n", ans[i]);
         printf("end\n");


     }
     return 0;
}
发布了40 篇原创文章 · 获赞 13 · 访问量 876

猜你喜欢

转载自blog.csdn.net/weixin_43891021/article/details/90581922