公路村村通 非常直白的最小生成树问题。
原题链接:PTA | 程序设计类实验辅助教学平台
题目描述
现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本。
输入格式
输入数据包括城镇数目正整数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
题目分析
浙大讲解视频:数据结构_浙江大学_中国大学MOOC(慕课)
对于输入样例,对每个村庄从0-5进行编号,将输入样例转化为图,如下图所示:
从节点0开始考虑,将其收录进MST(Minimum Spanning Tree 最小生成树)。在剩余节点中,预算最低的是节点5(插入边<0,5>),于是将5收录进MST。更新剩余节点到收录节点的预算值,重复以上步骤,直至所有节点都被收录进MST。收录过程如下图所示:
代码
参考浙大MOOC讲解视频,整合课程中提供的官方代码,使用C语言实现,通过平台测试。
图的两种表示方式
邻接矩阵表示:数据结构_浙江大学_中国大学MOOC(慕课)
邻接表表示:数据结构_浙江大学_中国大学MOOC(慕课)
Prim算法
邻接矩阵存储:数据结构_浙江大学_中国大学MOOC(慕课)
完整代码
#include <stdio.h>
#define MAXN 1000
#define INFINITY 65535
#define ERROR -1
typedef int Vertex; // 用顶点下标表示顶点,为整型
typedef int WeightType; // 边的权值设为整型
//------------------------------------
// 边的定义
typedef struct ENode *PtrToENode;
struct ENode
{
Vertex V1, V2;
WeightType Weight;
};
typedef PtrToENode Edge;
// 图节点的定义
typedef struct GNode *PtrToGNode;
struct GNode
{
int Nv, Ne;
WeightType G[MAXN][MAXN];
};
typedef PtrToGNode MGraph;
//------------------------------------
// 邻接点的定义
typedef struct AdjVNode *PtrToAdjVNode;
struct AdjVNode
{
Vertex AdjV; // 邻接点下标
WeightType Weight;
PtrToAdjVNode Next;
};
typedef struct Vnode
{
PtrToAdjVNode FirstEdge; // 边表头指针
} AdjList[MAXN];
// 以邻接表方式存储的图节点的定义
typedef struct LGNode *PtrToLGNode;
struct LGNode
{
int Nv, Ne;
AdjList G;
};
typedef PtrToLGNode LGraph;
//------------------------------------
MGraph CreateMGraph(int Nv);
void InsertEdgeToMGraph(MGraph Graph, Edge E);
MGraph BuildMGraph();
LGraph CreateLGraph(int Nv);
void InsertEdgeToLGraph(LGraph Graph, Edge E);
Vertex FindMinCost(MGraph Graph, WeightType cost[]);
int Prim(MGraph Graph);
int main() {
MGraph Graph = BuildMGraph();
int total_cost = Prim(Graph);
printf("%d\n", total_cost);
return 0;
}
MGraph CreateMGraph(int Nv) {
Vertex V, W;
MGraph Graph;
Graph = (MGraph)malloc(sizeof(struct GNode));
Graph->Nv = Nv;
Graph->Ne = 0;
// 默认顶点编号从0到Nv-1
for (V = 0; V < Graph->Nv; V++)
for (W = 0; W < Graph->Nv; W++)
Graph->G[V][W] = Graph->G[W][V] = INFINITY;
return Graph;
}
void InsertEdgeToMGraph(MGraph Graph, Edge E) {
E->V1--; E->V2--; // 村落编号从0到N-1进行编号
Graph->G[E->V1][E->V2] = E->Weight;
Graph->G[E->V2][E->V1] = E->Weight;
}
MGraph BuildMGraph() {
MGraph Graph;
Edge E;
Vertex V;
int Nv, i;
scanf("%d", &Nv);
Graph = CreateMGraph(Nv);
scanf("%d", &(Graph->Ne));
if (Graph->Ne > 0) {
E = (Edge)malloc(sizeof(struct ENode));
for (i = 0; i < Graph->Ne; i++) {
scanf("%d %d %d", &E->V1, &E->V2, &E->Weight);
InsertEdgeToMGraph(Graph, E);
}
}
return Graph;
}
LGraph CreateLGraph(int Nv) {
Vertex V;
LGraph Graph;
Graph = (LGraph)malloc(sizeof(struct LGNode));
Graph->Nv = Nv;
Graph->Ne = 0;
// 默认顶点编号从0到Nv-1
for (V = 0; V < Graph->Nv; V++)
Graph->G[V].FirstEdge = NULL;
return Graph;
}
void InsertEdgeToLGraph(LGraph Graph, Edge E) {
PtrToAdjVNode NewNode;
// 插入边<V1, V2>,为V2建立新的邻接点
NewNode = (PtrToAdjVNode)malloc(sizeof(struct AdjVNode));
NewNode->AdjV = E->V2;
NewNode->Weight = E->Weight;
// 将V2插入V1的表头
NewNode->Next = Graph->G[E->V1].FirstEdge;
Graph->G[E->V1].FirstEdge = NewNode;
// 无向图,插入<V2, V1>边
NewNode = (PtrToAdjVNode)malloc(sizeof(struct AdjVNode));
NewNode->AdjV = E->V1;
NewNode->Weight = E->Weight;
NewNode->Next = Graph->G[E->V2].FirstEdge;
Graph->G[E->V2].FirstEdge = NewNode;
}
// 返回未被收录顶点中cost最小者
Vertex FindMinCost(MGraph Graph, WeightType cost[]) {
Vertex MinV, V;
WeightType MinCost = INFINITY;
for (V = 0; V < Graph->Nv; V++) {
if (cost[V] != 0 && cost[V] < MinCost) {
MinCost = cost[V];
MinV = V;
}
}
if (MinCost < INFINITY)
return MinV;
else
return ERROR;
}
int Prim(MGraph Graph) {
WeightType cost[MAXN], TotalCost = 0;
Vertex parent[MAXN], V, W;
int VCount = 0; // 收录的顶点数
Edge E;
// 初始化,默认初始点下标为0
for (V = 0; V < Graph->Nv; V++) {
cost[V] = Graph->G[0][V];
parent[V] = 0; // 暂且定义所有顶点的父节点都是初始点0
}
// 创建包含所有顶点但没有边的图,注意用邻接表版本
LGraph MST = CreateLGraph(Graph->Nv);
E = (Edge)malloc(sizeof(struct ENode));
// 将初始点0收录进MST
cost[0] = 0;
VCount++;
parent[0] = -1; // 当前树根是0
while (1)
{
V = FindMinCost(Graph, cost);
if (V == ERROR)
break;
E->V1 = parent[V];
E->V2 = V;
E->Weight = cost[V];
InsertEdgeToLGraph(MST, E);
TotalCost += cost[V];
cost[V] = 0;
VCount++;
for (W = 0; W < Graph->Nv; W++)
if (cost[W] != 0 && Graph->G[V][W] < INFINITY) { // W是V的邻接点,且未被收录
if (Graph->G[V][W] < cost[W]) { // 考虑新加入节点W后,cost是否会减小
cost[W] = Graph->G[V][W]; // 更新cost和parent
parent[W] = V;
}
}
}
if (VCount < Graph->Nv)
TotalCost = ERROR;
return TotalCost;
}
测试结果
注意:节点编号从0到N-1编号,注意索引。