【并查集】HDU 1232 畅通工程

题目链接->http://acm.hdu.edu.cn/showproblem.php?pid=1232

HDU 1232   畅通工程                                                                                 

畅通工程


Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 61941    Accepted Submission(s): 33159

Problem Description

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

Input

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

3 3
1 2
1 2
2 1


这种输入也是合法的
当N为0时,输入结束,该用例不被处理。 

Output

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

Sample Input 

  4 2
 1 3
 4 3
 3 3
 1 2
 1 3
 2 3
 5 2
 1 2
 3 5
 999 0
 0

Sample Output


 1
 0
 2
 998

Problem Idea

 【题意】

    题意为:输入一个节点数为n,边数为m的无向图的各条边,共m条边的信息,即自己需要指定哪些起点到终点是连通的。

  要求你输出最少还需要建设的道路数目,即为实现该图的全连通而需要增加的“最小连通分量数”:

 【类型】

  并查集求无向图的连通分量

 【分析】

  并查集模板题,入门。

 【核心思路】

   设置一个变量cnt表示最少还需要建设的道路数目,即为实现该图的全连通而需要增加的“最小连通分量数”

   初始化:除去当前已有的1个节点,其他节点的连通分量数为n-1,则cnt初值为n-1

            通过遍历m条边,用cnt剪去这m条边能形成的连通分量数,则是最少还需要建设的道路数目

 //当前1个节点有1个连通分量
        int cnt=n-1;//cnt记录还需要建的路数目,即当前还需增加的连通分量数,初始化为n-1
        while(m--){
            int u,v;//起点和终点
            scanf("%d%d",&u,&v);
            cnt-=bind(u,v);//减去当前已有的连通分量数
        }
        printf("%d\n",cnt); 

 

Souce Code

#include <iostream>
#include <cstring>
#include <vector>
#include <cstdio>
using namespace std;
const int nmax=1000+5;
const int mmax=100000+5;
//并查集基本代码 模板
//fa需要初始化,memeset(fa,-1,sizeof(fa);
int fa[nmax];//fa[i]表示i节点的父节点

int findset(int x){//返回x节点的根节点,并把x节点直接挂在根节点下面
    //return fa[x]==-1 ? x :fa[x]=findset(x); 这一行错了,是fa[x]=findset(fa[x]),不是fa[x]=findset(x)
    return fa[x]==-1 ? x :fa[x]=findset(fa[x]);//也不能写成return fa[x]==-1?x:findset(fa[x]);
    //如果fa[x]==-1,说明x本身就是根
    //如果fa[x]!=-1,递归调用,返回x的父节点所在树的根
}

int bind(int u,int v){//合并u节点和v节点所属的连通分量
    int fu=findset(u);//获取u节点的根
    int fv=findset(v);//获取v节点的根
    if(fu!=fv){//若根不同,属于不同的连通分量,则可合并
        fa[fu]=fv;
        return 1;//连通分量少了1个
    }
    return 0;//若根相同连通分量数不变
}
int main() {
    int n,m;//n个节点,m条边
    while(scanf("%d",&n)==1 &&n>0){
        scanf("%d",&m);
        memset(fa,-1, sizeof(fa));//初始化fa数组
        //当前1个节点有1个连通分量
        int cnt=n-1;//cnt记录还需要建的路数目,即当前还需增加的连通分量数,初始化为n-1
        while(m--){
            int u,v;//起点和终点
            scanf("%d%d",&u,&v);
            cnt-=bind(u,v);//减去当前已有的连通分量数
        }
        printf("%d\n",cnt);
    }

    return 0;
}

new AC Code

还需要增加的边数(使得该图连通)=当前的连通分量数-1

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int nmax=1010;
int father[nmax];
int isRoot[nmax];

int findFather(int u){
	if(u==father[u]) return u;
	else{
		int f=findFather(father[u]);
		father[u]=f;
		return f;
	}
} 

void Union(int u,int v){
	int fu=findFather(u);
	int fv=findFather(v);
	if(fu!=fv){
		father[fu]=fv;
	} 
}

void init(int n){
	for(int i=1;i<=n;i++){
		father[i]=i;
		isRoot[i]=0;
	}
}

int main(int argc, char** argv) {
	int n,m;//点数,边数
	while(cin>>n>>m){
		if(n==0){
			break;
		}
		memset(father,0,sizeof(father));
		memset(isRoot,0,sizeof(isRoot));
		init(n);
		int u,v;
		for(int i=0;i<m;i++){
			cin>>u>>v;
			Union(u,v);
		}
		int ans=0;
		for(int i=1;i<=n;i++){
			if(father[i]==i){
				ans++;
			}
		}
		/*  //计算连通分量 
		int ans=0;
		for(int i=1;i<=n;i++){
			isRoot[findFather(i)]++;
		}
		for(int i=1;i<=n;i++){
			if(isRoot[i]!=0){
				ans++;
			}
		}*/
		//还需要增加的边数=连通分量数-1; 
		cout<<ans-1<<endl;
		
	} 
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值