HDOJ 3371 Connect the Cities

本文介绍HDOJ3371连接城市的算法问题,使用Kruskal算法解决最小生成树问题,确保所有幸存城市通过新建道路相互连接,同时考虑已有的连接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【题号】HDOJ 3371

【题目描述】

Connect theCities

Time Limit: 2000/1000 MS(Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 10733    Accepted Submission(s): 3056


Problem Description

In 2100, since thesea level rise, most of the cities disappear. Though some survived cities arestill connected with others, but most of them become disconnected. Thegovernment wants to build some roads to connect all of these cities again, butthey don’t want to take too much money.  

 

 

Input

The first linecontains the number of test cases.
Each test case starts with three integers: n, m and k. n (3 <= n <=500)stands for the number of survived cities, m (0 <= m <= 25000) stands forthe number of roads you can choose to connect the cities and k (0 <= k <=100) stands for the number of still connected cities.
To make it easy, the cities are signed from 1 to n.
Then follow m lines, each contains three integers p, q and c (0 <= c <=1000), means it takes c to connect p and q.
Then follow k lines, each line starts with an integer t (2 <= t <= n)stands for the number of this connected cities. Then t integers follow standsfor the id of these cities.

 

 

Output

For each case,output the least money you need to take, if it’s impossible, just output -1.

 

 

Sample Input

1

6 4 3

1 4 2

2 6 1

2 3 5

3 4 33

2 1 2

2 1 3

3 4 5 6

 

 

Sample Output

1

 

 

Author

dandelion

 

 

Source

HDOJ MonthlyContest – 2010.04.04

 

 

【类型】

最小生成树

【分析】

简单的求最小生成树,用Kruskal算法,特别注意:城市号是从1开始的,n个城市就是1~n。

 

【源代码】

/*
最小生成树 
Kruskal算法 
特别注意:城市号是从1开始的,n个城市就是1~n 
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm> 
using namespace std;
const int Max =  25000 + 5; 
int p[510]; //存储父节点
int hash[510];  

int find(int x) //并查集的find,查找x所在集合的代表元 
{
	return (p[x] == x) ? x : p[x] = find(p[x]);
	//这里顺便把遍历过的节点都变成树根的子节点	
}

struct edge
{
	int u,v,w; //u,v为两个端点,w为权重 
}G[Max]; 


int cmp(edge a, edge b)
{
	return a.w < b.w;
}

int main()
{
	int kase; //案例的组数
	int num; //需要连的线的条数 
	int st;
	int ct;
	int n,m,k,t;
	int res; //最小生成树的权重 
	scanf("%d",&kase);
	while(kase--)
	{
		num = 0; 
		int i; 
		scanf("%d %d %d",&n,&m,&k);
		memset(hash, 0, sizeof(hash));
		
		//初始化并查集 
		for(i=1; i<=n; i++)
		{
			p[i] = i;	
		}
		
		for(i=0; i<m; i++)
		{
			scanf("%d %d %d",&G[i].u,&G[i].v,&G[i].w);
		}
		//先把已经连上的边用并查集合并起来
		while(k--)
		{
			scanf("%d %d",&t,&st);
			t--;
			while(t--)
			{
				scanf("%d",&ct);
				p[find(st)] =  find(ct); //所有元素同一个根节点,也在同一个集合 
			}
		}
		//计算由n个节点、n-1条边组成的最小生成树还需要添加的边数
		num = 0;
		int h;//集合的代表元 
		for(i=1; i<=n; i++)  //看有多少个没连通的部分 
		{
			h = find(i); //i所在的集合的代表元 
			if(!hash[h])
			{
				hash[h] = 1;
				num++;
			}
		}
		//这里num肯定多算了一个
		num--;
		res = 0;
		sort(G, G+m, cmp);//先给边按权重从小到大排序 
		for(i=0; i<m; i++) //考虑选择性地添加m条边中的边 
		{
			//判断G[i]加入是否会造成回路,用并查集检测
			int x = find(G[i].u); //得到u的所在集合的代表元
			int y = find(G[i].v); //得到v的所在集合的代表元
			if(x != y) //如果u,v不在同一个集合,合并 
			{
				res += G[i].w;
				p[x] = y; //直接把x作为y的子节点,则两棵树就合并成一棵树 
				num--; 
			}
			if(num == 0) //已经连通
				break; 
		}
		if(num > 0)
			printf("-1\n");
		else
			printf("%d\n",res);	
	} 
	return 0;
}


       

也可以使用重载运算符,似乎更快一些:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm> 
using namespace std;
const int Max =  25000 + 5; 
int p[510],hash[510];  
int find(int x) 
{
	return (p[x] == x) ? x : p[x] = find(p[x]);	
}
struct edge
{
	int u,v,w;
	<span style="color:#ff0000;">bool operator <  (const edge& e) const
	{
		return w < e.w; 
	} </span>
}G[Max]; 
int main()
{
	int kase,num;,st,ct;
	int n,m,k,t;
	int res;
	scanf("%d",&kase);
	while(kase--)
	{
		num = 0; 
		int i; 
		scanf("%d %d %d",&n,&m,&k);
		memset(hash, 0, sizeof(hash));
		for(i=1; i<=n; i++)
		{
			p[i] = i;	
		}
		
		for(i=0; i<m; i++)
		{
			scanf("%d %d %d",&G[i].u,&G[i].v,&G[i].w);
		}
		while(k--)
		{
			scanf("%d %d",&t,&st);
			t--;
			while(t--)
			{
				scanf("%d",&ct);
				p[find(st)] =  find(ct); 
			}
		}
		num = 0;
		int h;
		for(i=1; i<=n; i++)  
		{
			h = find(i); 
			if(!hash[h])
			{
				hash[h] = 1;
				num++;
			}
		}
		num--;
		res = 0;
		<span style="color:#ff0000;">sort(G, G+m); </span>
		for(i=0; i<m; i++) 
		{
			int x = find(G[i].u); 
			int y = find(G[i].v);
			if(x != y) 
			{
				res += G[i].w;
				p[x] = y; 
				num--; 
			}
			if(num == 0)
				break; 
		}
		if(num > 0)
			printf("-1\n");
		else
			printf("%d\n",res);	
	} 
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值