1004: 惠民工程

题目描述

市政府“惠民工程”的目标是在全市n个居民点间之架设煤气管道(但不一定有直接的管道相连,只要能间接通过管道可达即可)。很显然最多可架设 n(n-1)/2条管道,然而实际上要连通n个居民点只需架设n-1条管道就可以了。现请你编写程序,计算出该惠民工程需要的最低成本。

输入

测试输入包含若干测试用例。每个测试用例的第1行给出居民点数目M ( < =100 )、 评估的管道条数 N;随后的 N 行对应居民点间管道的成本,每行给出一对正整数,分别是两个居民点的编号,以及此两居民点间管道的成本(也是正整数)。为简单起见,居民点从1到M编号。

输出

对每个测试用例,在1行里输出全市管道畅通所需要的最低成本。若统计数据不足以保证畅通,则输出“?”。

样例输入

3 3
1 2 1
1 3 2
2 3 4
3 1
2 3 2

样例输出

3
?

【思路】最小生成树,这里使用Prim算法实现.以某个顶点开始,依次向外拓展加边,直至所有的顶点都加入.close数组存储顶点集合到各个顶点的距离,注意未访问时添加.因为已经加入的顶点已经存了代价,相当于贪心,不用再检测,拿最新加入的顶点往外拓展加边从而加顶点.

【注】无向图,对称存储两遍.

#include<stdio.h>
#define INF 0x7ffff
int vis[101];//访问标记数组 
int MG[101][101];//邻接矩阵 
int prim(int M,int N){
	//close数组存放到集合到各个顶点的距离 
	int close[M];
	//M为顶点,N为边条数
	int ans=0;//ans为结果,若ans为-1,则不可行
	//先将第一个顶点加入
	for(int i=1;i<=M;i++){
		close[i]=MG[1][i];
	}
	vis[1]=1;//标记第一个点已访问
	//将剩余顶点依次添加,若某一次局部变量minCost为无穷大,则断掉,不能保证畅通
	for(int i=2;i<=M;i++){
		int minCost=INF;
		int index;//新添加的顶点
		//从第一个顶点开始,存储Cost
		for(int j=1;j<=M;j++){
			if(!vis[j]&&minCost>close[j]){
				//更新顶点及成本 
				minCost=close[j];
				index=j;
			}
		} 
		if(minCost==INF){//断掉了,不畅通 
			return -1;
		} 
		//将新加进来的顶点标记已读
		vis[index]=1;
		ans=ans+close[index];
		//更新close数组(通过中间点,close若大于通过中间点的成本) 
		for(int j=1;j<=M;j++){ 
            if(!vis[j]&&close[j]>MG[index][j]){ 
                close[j]=MG[index][j];
            } 
		} 
	} 
	return ans;

}
int main(void)
{
	int M,N; 
	//存入带权图
	while(scanf("%d%d",&M,&N)!=EOF){//M为顶点,N为边 
		int start,end,weight;
		//初始化邻接矩阵
		for(int i=1;i<=M;i++){
			for(int j=1;j<=M;j++){
				if(i==j) MG[i][j]=0;//自己到自己距离为0 
				else MG[i][j]=INF;//自己到其他初始化距离为无穷大 
			}
		}
		//初始化访问标记数组
		for(int i=1;i<=M;i++){
			vis[i]=0;//为0表示未访问 
		} 
		for(int i=1;i<=N;i++){
			scanf("%d%d%d",&start,&end,&weight);
			MG[start][end]=MG[end][start]=weight;//无向图,记得存两遍 
		}
		int res=prim(M,N);
		if(res==-1) printf("?\n");
		else printf("%d\n",res);  
	}
	return 0;
 } 
发布了44 篇原创文章 · 获赞 1 · 访问量 2291

猜你喜欢

转载自blog.csdn.net/Do_________/article/details/103858119