题目描述
市政府“惠民工程”的目标是在全市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;
}