HDU1232--畅通工程
某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。问最少还需要建设多少条道路?
1 2
1 2
2 1
这种输入也是合法的
当N为0时,输入结束,该用例不被处理。
4 2 1 3 4 3 3 3 1 2 1 3 2 3 5 2 1 2 3 5 999 0 0
所谓并查集呢,就是用一个数组,把一个元素的父节点存起来。父节点是什么呢,一般就是根据题目来说对当前点有指向关系的节点。 比如在这道题中,如果节点2和节点1相连,那么可以设节点1为节点2的父节点(反之也行)。那么当节点3和节点2相连时,并查集的作用就显现出来了, 节点3会与节点1相连。 并查集的思想是,若是两个节点相连,那么先找到他们的根节点,然后根节点相连。一个元素最初的根节点是自身。 如图
AC代码
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
int asd[1008]; //指向父节点
int get(int a)
{
int b=a;
while(asd[b]!=b) //寻找父节点
{
b=asd[b];
}
return b;
}
void conbin(int a,int b) //建立父节点联系
{
int a1=get(a);
int b1=get(b);
if(a1!=b1)
asd[a1]=b1;
}
int main()
{
int n,m,x,y,num,i;
while(scanf("%d",&n)&&n){
for(i=0;i<=n;i++)
asd[i]=i;
scanf("%d",&m);
for(int j=m;j>0;j--){
scanf("%d%d",&x,&y);
conbin(x,y);
}
num=-1;
for(i=1;i<=n;i++)
if(asd[i]==i)
num++;
cout<<num<<endl;
}
return 0;
}
那么再看一些模糊点的题目。。
HUD--3172 Virtual Friends
These days, you can do all sorts of things online. For example, you can use various websites to make virtual friends. For some people, growing their social network (their friends, their friends' friends, their friends' friends' friends, and so on), has become an addictive hobby. Just as some people collect stamps, other people collect virtual friends.
Your task is to observe the interactions on such a website and keep track of the size of each person's network.
Assume that every friendship is mutual. If Fred is Barney's friend, then Barney is also Fred's friend.
The first line of each case indicates the number of test friendship nest.
each friendship nest begins with a line containing an integer F, the number of friendships formed in this frindship nest, which is no more than 100 000. Each of the following F lines contains the names of two people who have just become friends, separated by a space. A name is a string of 1 to 20 letters (uppercase or lowercase).
1 3 Fred Barney Barney Betty Betty Wilma
2 3 4
输入的是字符串,我们需要用一个map来存放我们给这个字符串人为的编号。
其他的和上一题差不多。只需要用一个con[]数组记录以当前结点为根的集里有多少人就好了。
#include <iostream>
#include <cstdio>
#include <map>
#include <cstring>
using namespace std;
int f[100086];
int con[100086];
int fin(int a)
{
if(a==f[a])
{
return a;
}
return f[a]=fin(f[a]);
}
int main()
{
int t;
while(scanf("%d",&t)!=EOF)
{
while(t--)
{
int n,i;
scanf("%d",&n);
string xxx,yyy;
map<string,int> asd;
for(i=0;i<=n;i++)
{
f[i]=i;
con[i]=1;
}
asd.clear();
int low=1;
for(i=0;i<n;i++)
{
cin>>xxx>>yyy;
if(!asd[xxx])
{
asd[xxx]=low++;
}
if(!asd[yyy])
{
asd[yyy]=low++;
}
int a=asd[xxx];
int b=asd[yyy];
int x=fin(a);//这里把上一题的conbine函数写到main里面了
int y=fin(b);
if(x!=y)
{
f[x]=y;
con[y]+=con[x];
printf("%d\n",con[y]);
}
else
{
printf("%d\n",con[x]);
}
}
}
}
return 0;
}
HDU--1892 AbugsLife
Background
Professor Hopper is researching the sexual behavior of a rare species of bugs. He assumes that they feature two different genders and that they only interact with bugs of the opposite gender. In his experiment, individual bugs and their interactions were easy to identify, because numbers were printed on their backs.
Problem
Given a list of bug interactions, decide whether the experiment supports his assumption of two genders with no homosexual bugs or if it contains some bug interactions that falsify it.
2 3 3 1 2 2 3 1 3 4 2 1 2 3 4
Scenario #1:
Suspicious bugs found!
Scenario #2:
No suspicious bugs found!
Huge input,scanf is recommended.
这道题是带权值的并查集,需要一定的想法。当然可以按照二分图去做,不过这里讲并查集,就用并查集吧。
这里设置father[]为并查集数组,rank[]代表的是子节点到根节点的距离。rank[i]=0代表和根节点同性,为1则异性。
#include <iostream>
#include<cstdio>
#include<string>
#include<cstring>
using namespace std;
int Father[2005],Rank[2005];
bool flag;
int Find(int x)
{
if (Father[x]!=x)
{
int fx=Find(Father[x]);
Rank[x]=(Rank[x]+Rank[Father[x]])%2;//更新集合关系
Father[x]=fx;
}
return Father[x];
}
void Union(int x,int y)
{
int fx=Find(x),fy=Find(y);
if(fx==fy)
{
if(Rank[x]==Rank[y])
flag=true;
return;
}
Father[fy]=fx;
Rank[fy]=(Rank[x]+Rank[y]+1)%2;
}
int main()
{
int t;
cin>>t;
int i;
for (int cases=1;cases<=t;cases++)
{
flag=false;
int n,k;
for (i=0;i<2005;i++)
Father[i]=i,Rank[i]=0;
cin>>n>>k;
for (i=0;i<k;i++)
{
int x,y;
cin>>x>>y;
if (flag)
continue;
Union(x,y);
}
cout<<"Scenario #"<<cases<<":\n";
if (flag) cout<<"Suspicious bugs found!\n\n";
else cout<<"No suspicious bugs found!\n\n";
}
return 0;
}
设开始数据是这样,2,3,4,5都为1的子节点。
rank[i]=0 | 1 | 2 | 3 |
rank[i]=1 | 4 | 5 |
现在输入数据 6 3,那么就会变成
rank[i]=0 | 6 | 5 | 4 |
rank[i]=1 | 1 | 2 | 3 |
如果是3 6,那么就是
rank[i]=0 | 1 | 2 | 3 |
rank[i]=1
| 4 | 5 |
6
|
由此可见,两个数的相对顺序没有什么大的影响,不过经常变换的话运算次数会增多。
注意的是,上面的变换只在用到的时候才会进行,而不是全换的。
这看了三道题,并查集应该不在话下了吧?