【并查集】SOJ并查集篇

来源:互联网 发布:淘宝自动评价要几天 编辑:程序博客网 时间:2024/06/10 14:58

并查集模板(路径压缩+启发式合并)

#include <cstdio>#include <cstring>const int maxN=50000+50;int son[maxN];int father[maxN];bool vis[maxN];int Find(int x){return x==father[x]?x:father[x]=Find(father[x]);}void Join(int x,int y){int fax,fay;fax=Find(x);fay=Find(y);if (fax==fay){return;}else if (son[fax]>son[fay]){father[fay]=fax;son[fax]+=son[fay];return;}else{father[fax]=fay;son[fay]+=son[fax];return;}}

问题一 SOJ 2245

There are so many different religions in the world today that it is difficult to keep track of them all. You are interested in finding out how many different religions students in your university believe in.

You know that there are n students in your university (0 < n <= 50000). It is infeasible for you to ask every student their religious beliefs. Furthermore, many students are not comfortable expressing their beliefs. One way to avoid these problems is to ask m (0 <= m <= n(n-1)/2) pairs of students and ask them whether they believe in the same religion (e.g. they may know if they both attend the same church). From this data, you may not know what each person believes in, but you can get an idea of the upper bound of how many different religions can be possibly represented on campus. You may assume that each student subscribes to at most one religion.

Input

The input consists of a number of cases. Each case starts with a line specifying the integers n and m. The next m lines each consists of two integers i and j, specifying that students i and j believe in the same religion. The students are numbered 1 to n. The end of input is specified by a line in which n = m = 0.

Output

For each test case, print on a single line the case number (starting with 1) followed by the maximum number of different religions that the students in the university believe in.

Sample Input

10 91 21 31 41 51 61 71 81 91 1010 42 34 54 85 80 0

Sample Output

Case 1: 1Case 2: 7

Hint

Huge input, scanf is recommended.

Source

Alberta Collegiate Programming Contest 2003.10.18

分析:即求动态合并各种集合后总共有多少个集合

利用并查集进行合并  之后扫描每个元素的father进行计数  用vis数组防止同一father被多次计数

代码如下:

#include <cstdio>#include <cstring>const int maxN=50000+50;int son[maxN];int father[maxN];bool vis[maxN];int Find(int x){    return x==father[x]?x:father[x]=Find(father[x]);}void Join(int x,int y){    int fax,fay;    fax=Find(x);    fay=Find(y);    if (fax==fay)    {        return;    }    else if (son[fax]>son[fay])    {        father[fay]=fax;        son[fax]+=son[fay];        return;    }    else    {        father[fax]=fay;        son[fay]+=son[fax];        return;    }}int main(){    int n,m,i,temp,a,b,cas;    cas=1;    while(true)    {        scanf("%d%d",&n,&m);        if (!n && !m)        {            break;        }        for (i=1;i<=n;i++)        {            father[i]=i;            son[i]=1;            vis[i]=false;        }        while(m--)        {            scanf("%d%d",&a,&b);            Join(a,b);        }        int ans=0;        for (i=1;i<=n;i++)        {            temp=Find(i);            if (!vis[temp])            {                ans++;                vis[temp]=true;            }        }        printf("Case %d: %d\n",cas,ans);        cas++;    }    return 0;}


 

总结:在问题规模较大的情况下 使用路径压缩查找祖先 启发式合并合并集合 会有很高的效率优势

问题二 SOJ 2389: Journey to Tibet

After having to reinstall your computer 42 times on the same day, you decided to take a short break -- you will spend the next 5 years (or more) in Tibet. The population of China is very unevenly distributed: in the eastern coastal areas the population density can be above 400 people per square kilometer, while in the western plateaus there are less then 10 people per square kilometer. However, Tibet, where 2.3 million people share 1.2 million square kilometers, is by far the most sparsely populated place in China. You hope that in the small villages and monasteries of Tibet, no one will ask you to fix their computer.

But which village should you choose? Looking at the map of Tibet, you see a large number of interesting places. You want to go to a place where you can visit many monasteries. Each village has a number of monasteries. Your plan is that you go from village to village to visit as many monasteries as possible. However, you can only travel 30 kilometers a day, and it is not safe to spend the night in the wilderness. Thus, depending on your initial position, you can visit only some of the villages. Therefore, you have to choose your initial position (the village where you start your holiday) carefully, if you want to maximize the number of monasteries that can be visited.

A final note: Tibet enjoys(or, say, suffers) an average of 3,000 hours of sunshine a year, so don't forget your sun glasses and suntan cream!

Input

The input contains several blocks of test cases. Each case begins with a line containing an integer1 ≤n ≤ 1000, the number of villages. The nextn lines contains three integers each: the two coordinates of the village (in kilometers), and the number of monasteries in the village. The villages are numbered from 1 ton, in the order that they appear in the input. The coordinates are between 0 and 20000, and the number of monasteries in a village is at most l000. The input is terminated by a block with 0 villages.

Output

For each test case you have to output two integers on a line (separated by a single space). The first integer identifies the village where you want to go (this number is between 1 andn), and the second integer is the number of monasteries that can be visited starting from this location. If there is more than one village that maximizes the number of reachable monasteries, choose the one that has the smallest index (you know, we problemsetters hate to write any special judge).

Input

6100 100 80 0 100 10 310 30 41000 1000 4100 128 30

Sample Output

2 17

 

问题重述:将坐标系距离不超过30的点合并到同一集合 找出son最多的集合中最小编号的结点

分析:合并的时候不使用启发式合并 而使用编号小的作为集合的father进行合并 最后先扫描一遍son找出最大的son即其father 然后扫描一遍father即可

代码如下

#include <cstdio>#include <cstring>const int maxN=1000+50;int son[maxN];int father[maxN];bool vis[maxN];typedef struct  {int X;int Y;int N;}Node;Node villge[maxN];int Find(int x){return x==father[x]?x:father[x]=Find(father[x]);}void Join(int x,int y){int fax,fay;fax=Find(x);fay=Find(y);if (fax==fay){return;}else if (fax<fay){father[fay]=fax;son[fax]+=son[fay];return;}else{father[fax]=fay;son[fay]+=son[fax];return;}}int main(){int n,m,i,j,temp;while(true){scanf("%d",&n);if (!n){break;}for (i=1;i<=n;i++){scanf("%d%d%d",&villge[i].X,&villge[i].Y,&villge[i].N);son[i]=villge[i].N;father[i]=i;}for (i=1;i<=n;i++){for (j=i+1;j<=n;j++){temp=(villge[i].X-villge[j].X)*(villge[i].X-villge[j].X)+(villge[i].Y-villge[j].Y)*(villge[i].Y-villge[j].Y);if (temp<=900){Join(i,j);}}}int MAX=0;int t=1;for (i=1;i<=n;i++){if (son[i]>MAX){MAX=son[i];t=i;}}for (i=1;i<=n;i++){if (father[i]==t){break;}}//  for (i=1;i<=n;i++)//  {//  printf("%d %d\n",father[i],son[father[i]]);// }printf("%d %d\n",i,MAX);}return 0;}


总结:注意考虑避免出现MAX无法更新的情况 即值为0

问题三 2490: Math teacher's test

Description

Bob is good at logic. Usually, when he takes exams, he can guess out the correct answer even if he does not know what the question really means:-)
His math teacher learnt his talent in logic, and decided to have a test. He gave Bob a lot many multi-choice questions, and Bob should solve all of them.
Questions are numbered from 0 to n-1. Bob found that all of the questions have the same form:

(i) The correct answer to this question is().    A. The correct answer to Question j is C1;    B. The correct answer to Question k is C2.

Here C1 and C2 are either A or B, and i, j, k are the sequence number of questions.
Every question can have one and only one correct answer.
Bob bent his head over the test, and he found no matter what he chose, the answers came to conflict at the end. So he decided to refer to you. Given descriptions of all questions, you should tell him whether or not the test has a solution. If yes, he will solve it by himself:-)

Input

The input contains multiple test cases.
The first line of each case is an integer n( 1<=n<=20000 ), which indicates the number of questions.
Then follows n lines, each containing four integers:
a0 b0 a1 b1
where "a0 b0" describes Choice A, and "a1 b1" describes Choice B. a0 and a1 are the sequence number of questions. b0 and b1 indicates the choice, 0 stands for A, and 1 stands for B.
EOF ends the input.

Output

If all the questions do not conflict, output "Yes". Otherwise, output "No".

Sample Input

10 0 0 110 1 0 0

Sample Output

YesNo

Source

 

分析:此题也可用2-SAT解决

每道题可以有 0 1两种选项 因此有2*n个结点

结点i*2 表示 i题选0   

结点i*2+1表示 i题选1

那么当结点i*2选0时 则与0选项对应的结点应该加入同一集合 表示为同一种选择方式

最后判断每一对i的两个结点是否为同一集合 如果为同一集合 则出现矛盾

代码如下:

#include <cstdio>
#include <cstring>
#include <map>
#include <string>
#include <vector>
using namespace std;
const int maxN=50000+50;


int son[maxN];
int father[maxN];

int Find(int x)
{
 return x==father[x]?x:father[x]=Find(father[x]);
}

void Join(int x,int y)
{
 int fax,fay;
 fax=Find(x);
 fay=Find(y);
 if (fax==fay)
 {
  return;
 }
 else if (son[fax]>=son[fay])
 {
  father[fay]=fax;
  son[fax]+=son[fay];
  return;
 }
 else
 {
  father[fax]=fay;
  son[fay]+=son[fax];
  return;
 }
}


int main()
{
    int n,i;
    while(scanf("%d",&n)==1)
    {
        int x,y;
        for(i=0;i<2*n;i++)
        {
            father[i]=i;
   son[i]=1;
        }
        for(i=0;i<n;i++)
        {
            scanf("%d%d",&x,&y);
            Join(i*2,x*2+y);
            scanf("%d%d",&x,&y);
            Join(i*2+1,x*2+y);
        }
        int flag=0;
        for(i=0;i<n;i++)
        {
            if(Find(i*2)==Find(i*2+1))
            {
                flag=1;
                break;
            }
        }
        if(flag)
            printf("No\n");
        else
            printf("Yes\n");
    }
 return 0;
}

 

 

 

0 0