08-图7 公路村村通
分数 30 作者 陈越 单位 浙江大学
现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本。
输入格式:
输入数据包括城镇数目正整数N(≤1000)和候选道路数目M(≤3N);随后的M行对应M条道路,每行给出3个正整数,分别是该条道路直接连通的两个城镇的编号以及该道路改建的预算成本。为简单起见,城镇从1到N编号。
输出格式:
输出村村通需要的最低成本。如果输入数据不足以保证畅通,则输出−1,表示需要建设更多公路。
输入样例:
6 15
1 2 5
1 3 3
1 4 7
1 5 4
1 6 2
2 3 4
2 4 6
2 5 2
2 6 6
3 4 6
3 5 1
3 6 1
4 5 10
4 6 8
5 6 3
输出样例:
12
代码长度限制:16 KB 时间限制:400 ms 内存限制:64 MB
答案:
此图为图论中经典的“最小生成树”问题,这里采用进行Prim算法实现。
算法解析:
Prim算法的思想是让一棵小树长大,在这里我们先举一个“栗子”:
但是如果一个图一开始并不是连通的,那么也不存在最小生成树的说法,例如下面这个图:
所以我们在生成图的最小生成树过程中,还将会对图是否连通进行判断:若最后生成的树包含了所有的顶点,那么“公路村村通实现”,否则不实现。
代码展示:
# include<stdio.h>
# include<stdlib.h>
# include<stdbool.h>
# define MaxVexNum 1000
# define MaxWeight 65535
# define ERROR -1
// 图结构(采用邻接矩阵存储边,且从[1,1]开始存储,为了对应城镇从1开始编号)
typedef struct MGraphNode* MGraph;
struct MGraphNode{
int Martrix[MaxVexNum+1][MaxVexNum+1];
int Nv; // 顶点数
int Ne; // 边数
};
MGraph CreateMGraph();
int Prim(MGraph Graph);
int FindDistMin(int dist[],MGraph Graph);
int main(){
// 创建图
MGraph Graph = CreateMGraph();
// 采用Prim算法生成最小树,并返回权重(无法生成则返回0)
int Weight = Prim(Graph);
// 输出结果
printf("%d",Weight);
// 释放图
free(Graph);
return 0;
}
// 创建图
MGraph CreateMGraph(){
int Nv,Ne;
scanf("%d %d",&Nv,&Ne);
MGraph Graph = (MGraph)malloc(sizeof(struct MGraphNode));
// 初始化顶点和边信息
Graph->Nv = Nv;
Graph->Ne = Ne;
// 初始化邻接矩阵,为“正无穷大”(注意顶点是从1开始编号的)
int i,j;
for(i=1;i<=Graph->Nv;i++){
for(j=1;j<=Graph->Nv;j++){
Graph->Martrix[i][j] = MaxWeight;
}
}
// 插入边,无向边插入两次
int V1,V2,Weight;
for(i=0;i<Graph->Ne;i++){
scanf("%d %d %d",&V1,&V2,&Weight);
Graph->Martrix[V1][V2] = Weight;
Graph->Martrix[V2][V1] = Weight;
}
return Graph;
}
// Prim算法生成最小树
int Prim(MGraph Graph){
// 创建收录数组(从下标1开始存储),准备收录结点1为树根结点
int dist[Graph->Nv+1];
int i;
for(i=1;i<=Graph->Nv;i++){
dist[i] = Graph->Martrix[1][i];
}
// 分别记录收录的结点数目与最小树的权重
int Count=0,Weight=0;
// 收录结点1
dist[1] = 0;
Count++;
// 开始收录
while(1){
// 在未被收录的顶点中选取一个与树连接且离树最近的一个顶点下标
int MinV = FindDistMin(dist,Graph);
if(MinV==ERROR)break;
// 将其收录,累计收录结点数与最小树的权重
Count++;
Weight += dist[MinV];
dist[MinV] = 0;
// 更新未被收录的MinV的邻接点的离树的权重
for(i=1;i<Graph->Nv+1;i++){
if( Graph->Martrix[MinV][i] < MaxWeight && dist[i]!= 0 ){
// 是MinV的邻接结点,且未被收入,则更新
if(dist[i] > Graph->Martrix[MinV][i]){
dist[i] = Graph->Martrix[MinV][i];
}
}
}
}
// 图中所有顶点均在最小生成树中,则返回最终权重;否则返回ERROR
if(Count!=Graph->Nv)return ERROR;
else return Weight;
}
// 在未被收录的顶点中选取一个与树连接且离树最近的一个
int FindDistMin(int dist[],MGraph Graph){
int MinV,V = MaxWeight;
int i;
for(i=1;i<Graph->Nv+1;i++){
if( dist[i] != 0 && dist[i]<V){
// 未被收录且权重更小
V = dist[i];
MinV = i;
}
}
// 找到返回下标,找不大返回ERROR
if(V==MaxWeight) return ERROR;
else return MinV;
}