并查集模板(路径压缩+启发式合并)
#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 9 1 2 1 3 1 4 1 5 1 6 1 7 1 8 1 9 1 10 10 4 2 3 4 5 4 8 5 8 0 0
Sample Output
Case 1: 1 Case 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
6 100 100 8 0 0 10 0 10 3 10 30 4 1000 1000 4 100 128 3 0
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
1 0 0 0 1 1 0 1 0 0
Sample Output
Yes No
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;
}