hrbust 哈理工oj 2026 势力较量【并查集】

势力较量
Time Limit: 1000 MS Memory Limit: 32768 K
Total Submit: 77(33 users) Total Accepted: 45(31 users) Rating:  Special Judge: No
Description

在战火纷乱的古代有许多小国家。在这些小国家中,就会有一些强大的,一些弱小的。势力大的就会吞并势力小的,

形成更大的势力国家。现在给出你一些目前的国家势力关系,你能预算出以后的局势吗?


为了简化问题,给每个国家编号,像“国家1”, “国家2”......“国家N”

国家的较量首先从人数上进行比较,人数多的能打赢人数少的。如果两个国家的人数相同,就根据国家头目的编号来判断,

我们假设编号大的国家能打得过编号小的国家。

当两个国家相遇的时候,就会有一场打斗,且一定要分出胜负,输的就会归顺于赢得, 形成一个新的国家。
Input
多组测试数据,处理到文件结束。对于每组数据:
第一行两个整数N,M。N为国家个数,M为关系数目。
接下来M行,每行两个整数u, v。表示国家u,国家v相遇,有一场打斗。
然后试一个整数Q,表示Q个询问。
接下来Q行,每行一个整数S。
(N<100, M < 100, Q < 100)
Output
Case k:
对于每个询问,输出两个整数,
第一个整数表示S所在国家的头目,第二个整数表示S所在国家里边一共有多少个小国家。
Sample Input
5 3
1 2
1 3
4 5
2
1
5


5 4
1 2
1 3
1 4
1 5
4
2
3
4
5
Sample Output
Case 1:
2 3
5 2
Case 2:
2 5
2 5
2 5
2 5
Source
2014 Winter Holiday Contes 4

问题分三个部分来详解,初始化部分、merge部分、询问部分、

首先我们这里关系到连通路点集合问题(某一集合中元素个数)、所以我们需要两个数组,F【】、J【】、分别表示father和集合元素个数、

merge部分:

按照题意来模拟,首先判断人数、然后判断头目编号、

我们这里把J【find(x)】用来表示x所在国家(集合)一共有多少个元素(小国家)、当merge的时候,如果是F[Y]=X(让Y的爹是X)同时我们需要J[X]+=J[Y];

int find(int x)
{
    return f[x] == x ? x : (f[x] = find(f[x]));
}
void merge(int a,int b)
{
    int A,B;
    A=find(a);
    B=find(b);
    if(A!=B)
    {
        f[B]=A;
        j[A]+=j[B];//0.0
    }
}
知道了J【】数组是如何更新数据的,我们这里对题干要求去做就相对简单的多了:

1、按照人数多少判断谁赢:给出x,y。我们对应找到J【find(x)】和J【find(y)】、然后判断谁大谁小就行了、

2、如果人数相等按照头目编号来判断谁赢:直接判断find(x)和find(y)就行了:

       while(m--)
       {
           int x,y;
           scanf("%d%d",&x,&y);
           if(j[find(x)]>j[find(y)])//x的人数多于y
           {
               merge(x,y);
               continue;
           }
           if(j[find(y)]>j[find(x)])//y的人数多于x
           {
               merge(y,x);
               continue;
           }
           if(j[find(x)]==j[find(y)])//人数相等的时候
           {
               int X=find(x);
               int Y=find(y);
               if(X>Y)//如果x头目编号大于y
               {
                   f[Y]=X;
                   j[X]+=j[Y];
               }
               if(Y>X)//注意这里不能用else,我用了一发else,实力WA、如果X,Y同集合,就不用动了,我这里疏忽了
               {
                   f[X]=Y;
                   j[Y]+=j[X];
               }
           }
       }

最后的查询部分相对就容易的多了,最后上完整的AC代码:

#include
#include
using namespace std;
int f[1000];
int j[1000];
int find(int x)
{
    return f[x] == x ? x : (f[x] = find(f[x]));
}
void merge(int a,int b)
{
    int A,B;
    A=find(a);
    B=find(b);
    if(A!=B)
    {
        f[B]=A;
        j[A]+=j[B];
    }
}
int main()
{
   int n,m;
   int kase=0;
   while(~scanf("%d%d",&n,&m))
   {
       for(int i=0;i<=n;i++)
       {
           f[i]=i;
           j[i]=1;
       }
       while(m--)
       {
           int x,y;
           scanf("%d%d",&x,&y);
           if(j[find(x)]>j[find(y)])
           {
               merge(x,y);
               continue;
           }
           if(j[find(y)]>j[find(x)])
           {
               merge(y,x);
               continue;
           }
           if(j[find(x)]==j[find(y)])
           {
               int X=find(x);
               int Y=find(y);
               if(X>Y)
               {
                   f[Y]=X;
                   j[X]+=j[Y];
               }
               if(Y>X)
               {
                   f[X]=Y;
                   j[Y]+=j[X];
               }
           }
       }
       int q;
       scanf("%d",&q);
       printf("Case %d:\n",++kase);
       while(q--)
       {
           int s;
           scanf("%d",&s);
           printf("%d %d\n",find(s),j[find(s)]);
       }
   }
}





你可能感兴趣的:(并查集)