畅通工程

某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。问最少还需要建设多少条道路?

Input

测试输入包含若干测试用例。每个测试用例的第1行给出两个正整数,分别是城镇数目N ( < 1000 )和道路数目M;随后的M行对应M条道路,每行给出一对正整数,分别是该条道路直接连通的两个城镇的编号。为简单起见,城镇从1到N编号。
注意:两个城市之间可以有多条道路相通,也就是说
3 3
1 2
1 2
2 1
这种输入也是合法的
当N为0时,输入结束,该用例不被处理。

Output

对每个测试用例,在1行里输出最少还需要建设的道路数目。

Sample

InputOutput
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 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龙星尘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值