(hdu step 6.1.7)Connect the Cities(在有的路已经修建好的情况下,求让n个点连通的最小费用)

题目:

Connect the Cities

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 391 Accepted Submission(s): 139
 
Problem Description
In 2100, since the sea level rise, most of the cities disappear. Though some survived cities are still connected with others, but most of them become disconnected. The government wants to build some roads to connect all of these cities again, but they don’t want to take too much money.  
 
Input
The first line contains 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 for the 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 stands for 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 Monthly Contest – 2010.04.04
 
Recommend
lcy
 


题目分析:

              求最小生成树。这道题用kruscal来做会TLE。毕竟kruscal适用于稀疏图,这道题的便可能会比较多一点。然后改用prim来做,用C++提交就能AC了(杭电的服务器感觉最近是不是有点。。。同样的代码第一次交是TLE(>1000ms),第二次交就493ms,这是不是差的略多一点。这会让人崩溃的。。。快哭了)。

需要注意一下的是这道题关于“已建路”的输入数据格式:


例如样例中的:

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

从“2 1 2”开始就是已建路的数据。其意思为点1与点2之间的路已经建好。后面的意思是点1与点3的路已经建好,

在后面的就是点4 与点5 与点6之间连通。


代码如下:

/*
 * g1.cpp
 *
 *  Created on: 2015年3月10日
 *      Author: Administrator
 */



#include <iostream>
#include <cstdio>
#include <string>

using namespace std;

const int maxn = 501;
const int inf = 999999;


int map[maxn][maxn];
bool p[maxn];
int dis[maxn];
int pre[maxn];

int n;

int prim(){
	int sum = 0;

	int i;
	for(i = 2 ; i <= n ; ++i){
		p[i] = false;
		dis[i] = map[1][i];
		pre[i] = 1;
	}

	dis[1] = 0;
	p[1] = true;
	pre[1] = 1;

	for(i = 1 ; i <= n-1 ; ++i){
		int min = inf;
		int k = 0;

		int j;
		for(j = 1 ; j <= n ; ++j){
			if(!p[j] && dis[j] < min){
//				dis[j] = min;
				min = dis[j];
				k = j;
			}
		}


		if(k == 0){//没有点可以拓展,图不连通,无法求最小生成树
			return -1;//直接返回-1.
		}

		sum += dis[k];
		p[k] = true;

		for(j = 1 ; j <= n ; ++j){
			if(!p[j] && dis[j] > map[k][j]){
				dis[j] = map[k][j];
				pre[j] = k;
			}
		}
	}

	return sum;
}




int main(){
	int t;
	scanf("%d",&t);
	int tmp[maxn];

	while(t--){
		int m,k;

		scanf("%d%d%d",&n,&m,&k);

		int i,j;
		for(i = 1 ; i <= n ; ++i){
			for(j = 1 ; j <= n ; ++j){
				if(i == j){
					map[i][j] = 0;
				}else{
					map[i][j] = inf;
				}
			}
		}

		for(i = 1 ; i <= m ; ++i){
			int a,b,c;
			scanf("%d%d%d",&a,&b,&c);

			if(map[a][b] > c){
				map[a][b] = map[b][a] = c;
			}
		}

		//对于已经建好的路的数据的处理.(需要注意一下,跟之前的题目给出的形式不太一样)
		for(i = 1 ; i <= k ; ++i){
			int size;
			scanf("%d",&size);
//			int tmp[size];

			for(j = 0 ; j < size ; ++j){
				scanf("%d",&tmp[j]);
			}


			for(j = 0 ; j < size-1 ; ++j){
				map[tmp[j]][tmp[j+1]] = 0;
				map[tmp[j+1]][tmp[j]] = 0;
			}


		}

		printf("%d\n",prim());
//		printf("%d\n",prim1());
	}

	return 0;
}


以下是用kruscal来做的TLE的版本:

/*
 * g.cpp
 *
 *  Created on: 2015年3月10日
 *      Author: Administrator
 */

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>

using namespace std;

const int maxn = 505;

struct Edge{
	int begin;
	int end;
	int weight;
}edges[maxn*maxn];

int father[maxn];
int n;

int find(int a){
	if(a == father[a]){
		return a;
	}

	return father[a] = find(father[a]);
}

int kruscal(int count){
	int i;
	for(i = 1 ; i <= n ; ++i){
		father[i] = i;
	}


	int counter = 0;

	int sum = 0;
	for(i = 1 ; i <= count ; ++i){
		if(counter == n-1){
			break;
		}

		int fa = find(edges[i].begin);
		int fb = find(edges[i].end);

		if(fa != fb){
			father[fa] = fb;
			sum += edges[i].weight;

			counter++;
		}
	}

//	printf("%d\n",counter);

	return sum;
}

bool cmp(Edge a,Edge b){
	return a.weight < b.weight;
}


int main(){
	int t;
	scanf("%d",&t);

	int tmp[maxn];

	while(t--){
		int m,k;

		scanf("%d%d%d",&n,&m,&k);

		int cnt = 1;

		int i;
		for(i = 1 ; i <= m ; ++i){
			scanf("%d%d%d",&edges[cnt].begin,&edges[cnt].end,&edges[cnt++].weight);
		}

		for(i = 1 ; i <= k ; ++i){
		    int size;
			scanf("%d",&size);

//			int tmp[size];
//			vector<int> tmp;

			int j;
			for(j = 0 ; j < size ; ++j){
				scanf("%d",&tmp[j]);
			}

			for(j = 0 ; j < size-1 ; ++j){
				edges[cnt].begin = tmp[j];
				edges[cnt].end = tmp[j+1];
				edges[cnt++].weight = 0;
			}
		}

		cnt -= 1;

		sort(edges+1,edges+1+cnt,cmp);

		printf("%d\n",kruscal(cnt));
	}

	return 0;
}





如果对prim算法不太熟悉的可以拿这道题练习一下。它是一道求最小生成树的裸题:

http://www.sdutacm.org/sdutoj/problem.php?action=showproblem&problemid=2144


这道题的AC代码是:

/*
 * t.cpp
 *
 *  Created on: 2015年3月10日
 *      Author: Administrator
 */


#include <iostream>
#include <cstdio>
#include <cstring>


using namespace std;

const int maxn = 101;
const int inf = 99999;

int map[maxn][maxn];
bool p[maxn];
int dis[maxn];
int pre[maxn];


int n,m;

int prim(){
	int sum = 0;

	int i;
	for(i = 2 ; i <= n ; ++i){
		p[i] = false;
		dis[i] = map[1][i];
		pre[i] = 1;
	}

	p[1] = true;
	dis[1] = 0;

	for(i = 1 ; i <= n-1 ; ++i){
		int min = inf;
		int k = 0;

		int j;
		for(j = 1 ; j <= n ; ++j){
			if(!p[j] && dis[j] < min){
				min = dis[j];
				k = j;
			}
		}


		if(k == 0){
			return -10;
		}

		sum += dis[k];
		p[k] = true;

		for(j = 1 ; j <= n ; ++j){
			if(!p[j] && dis[j] > map[k][j]){
				dis[j] = map[k][j];
				pre[j] = k;
			}
		}
	}

	return sum;
}


int main(){
	while(scanf("%d%d",&n,&m)!=EOF){
//		memset(map,inf,sizeof(map));
		memset(p,false,sizeof(p));
		int i;
		int j;
		for(i = 1 ; i <= n ; ++i){
			for(j = 1 ; j <= n ; ++j){
				if(i == j){
					map[i][j] = 0;
				}else{
					map[i][j] = inf;
				}
			}
		}

		for(i = 1 ; i <= m ; ++i){
			int a,b,c;

			scanf("%d%d%d",&a,&b,&c);
			if(map[a][b] > c){
				map[a][b] = map[b][a] = c;
			}
		}

		printf("%d\n",prim());
	}

	return 0;
}








  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

帅气的东哥

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

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

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

打赏作者

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

抵扣说明:

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

余额充值