某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。问最少还需要建设多少条道路?
Input
测试输入包含若干测试用例。每个测试用例的第1行给出两个正整数,分别是城镇数目N ( < 1000 )和道路数目M;随后的M行对应M条道路,每行给出一对正整数,分别是该条道路直接连通的两个城镇的编号。为简单起见,城镇从1到N编号。
注意:两个城市之间可以有多条道路相通,也就是说
3 3
1 2
1 2
2 1
这种输入也是合法的
当N为0时,输入结束,该用例不被处理。
Output
对每个测试用例,在1行里输出最少还需要建设的道路数目。
Sample
Input | Output |
---|---|
4 2 1 3 4 3 3 3 1 2 1 3 2 3 5 2 1 2 3 5 999 0 0 | 1 0 2 998 |
以上就是我们今天要讲的题目,看清楚了吧,是一道并查集的类型题,前几天刚刚讲了一个“并查集的基本操作”。
并查集的基本操作http://t.csdn.cn/bI0ae 因为以前讲过,所以我们今天就来运用我之前写的并查集的许多操作,有init初始化操作,insert插入函数,find查找加路径压缩函数,unionSet合并两个集合函数,print输出并查集函数。
这道题,我们怎么运用呢?
根据输入,首先输入了n,就将1到n内所有数字插入并查集中,现在并查集中每个数字都是一个集合,然后输入m组合并操作,每次进行unionSet(a,b)函数操作来合并,合并之后不要忘了运用find函数进行路径压缩算法。
进行了这些操作之后,并查集里面有多个集合,如果只有一个集合那就代表不需要道路相连,只需要输出一个0即可,如果当前并查集有多个集合,就需要进行合并操作,首先,我们需要找到目前集合最大的那个集合的代表,然后将其他没有和这个代表相连的元素进行unionSet函数,千万别忘了find路径压缩,执行一次unionSet操作,就将sum++,相当于连了一条道路。
就这样,这道题的基本思路就讲到这里了,我们来看代码。
畅通工程:
#include<bits/stdc++.h>
using namespace std;
template<class T>
struct DisjointSet{
int *parent;
T *data;
map<T,int> m;
int capacity;
int size;
DisjointSet(int max=10){
capacity=max;
size=0;
parent=new int[max+1];
data=new T[max+1];
}
~DisjointSet(){
delete [] parent;
delete [] data;
}
void init(int max){
capacity=max;
size=0;
parent=new int[max+1];
data=new T[max+1];
}
bool insert(T x){
if(size==capacity) return false;
size++;
data[size]=x;
parent[size]=-1;
m[x]=size;
return true;
}
int getIndex(T x){
for(int i=1;i<=size;i++)
if(data[i]==x)
return i;
return -1;
}
int find(T x){
typename map<T,int>::iterator it;
it=m.find(x);
if(it==m.end()) return -1;
int i,rt;
i=rt=it->second;
while(parent[rt]>0)
rt=parent[rt];
int tmp;
for(;i!=rt;i=tmp){
tmp=parent[i];
parent[i]=rt;
}
return rt;
}
void unionSet(T x,T y){
int rx,ry;
rx=find(x);
ry=find(y);
if(rx==-1||ry==-1) return ;
if(rx==ry) return ;
if(parent[rx]<parent[ry]){
parent[rx]+=parent[ry];
parent[ry]=rx;
}
else{
parent[ry]+=parent[rx];
parent[rx]=ry;
}
}
void print(){
for(int i=1;i<=size;i++)
cout<<i<<"\t";
cout<<endl;
for(int i=1;i<=size;i++)
cout<<parent[i]<<"\t";
cout<<endl;
for(int i=1;i<=size;i++)
cout<<data[i]<<"\t";
cout<<endl;
}
};
int main(){
DisjointSet<int> s;
int n,m,sum;
while(cin>>n){
s.init(100);
sum=0;
if(n==0) return 0;
for(int i=1;i<=n;i++) s.insert(i);
cin>>m;
for(int i=0;i<m;i++){
int a,b;
cin>>a>>b;
s.unionSet(a,b);
}
if(m==0){
cout<<n-1<<endl;
continue;
}
int min=0x7f7f7f7f,mx;
for(int i=1;i<=n;i++)
if(s.parent[i]<min)
min=s.parent[i],mx=i;
for(int i=1;i<=n;i++)
if(s.parent[i]!=mx&&(s.parent[i]!=s.parent[mx]&&i!=mx))
s.unionSet(s.data[i],s.data[mx]),s.find(s.data[i]),sum++;
cout<<sum<<endl;
}
return 0;
}
注意啊,我们需要很多个特判(就是用来判断如果是当前跟的话就直接跳过)!
今天就讲到这里了,等会儿我讲解第二道题。
该题链接:
畅通工程 - HDU 1232 - Virtual Judgehttps://vjudge.net/problem/HDU-1232#google_vignette