题目:现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本。
输入格式:
输入数据包括城镇数目正整数N(≤1000)和候选道路数目M(≤3N);随后的M行对应M条道路,每行给出3个正整数,分别是该条道路直接连通的两个城镇的编号以及该道路改建的预算成本。为简单起见,城镇从1到N编号。
输出格式:
输出村村通需要的最低成本。如果输入数据不足以保证畅通,则输出−1,表示需要建设更多公路。
输入样例:
输出样例:
12
题解:
#include<stdio.h>
#include<stdlib.h>
#define MaxVertexNum 1001
#define INFINITY 65535
#define ERROR -1
typedef int WeightType;//连接矩阵的类型,权重
typedef int Vertex;/* 用顶点下标表示顶点,为整形 */
typedef int DataType;/* 数据类型 */
typedef struct GNode *PtrToGNode;//定义point to GNode的指针 PtrToGNode
struct GNode{
int Nv; /* 顶点数 */
int Ne; /* 边数 */
WeightType G[MaxVertexNum][MaxVertexNum];
DataType Data[];/* 有时会需要存顶点的数据 */
};
typedef PtrToGNode MGraph; /* 以邻接矩阵存储的图的类型 */
typedef struct AdjVNode *PtrToAdjVNode;
struct AdjVNode
{
Vertex AdjV; /* 邻接点的下标 */
WeightType Weight;//权重
PtrToAdjVNode Next;//next啥意思还不知道?
}; //typedef写的乱主要是浙大老师写的太花了,就乱一点巩固typedef语法吧
/* 定义邻接表类型AdjList */
typedef struct Vnode
{
PtrToAdjVNode FirstEdge;//指向第一条边结点的指针
DataType Data;//要是顶点还要存东西就存这里
}AdjList[MaxVertexNum];
/* 以邻接表的方式存储的图类型 */
typedef struct G2Node *PtrToG2Node;//定义point to GNode的指针 PtrToGNode
struct G2Node{
int Nv; /* 顶点数 */
int Ne; /* 边数 */
AdjList G; /* 邻接表 */
};
typedef PtrToG2Node LGraph; /* 以邻接表存储的图的类型 */
typedef struct ENode *PtrToENode;
struct ENode{
Vertex V1,V2; /* 有向边<V1,V2> */
WeightType Weight;/* 权重 */
};
typedef PtrToENode Edge; /* 连接线的类型 */
MGraph CreateGraph(int VertexNum);//初始化图
void InsertEdge(MGraph Graph,Edge E); //插入连接线
MGraph BuildGraph();
Vertex FindMinDist( MGraph Graph, WeightType dist[] );
int Prim( MGraph Graph, LGraph MST );
LGraph CreateGraph2(int VertexNum);
int main(){
LGraph MST;
MGraph MG = BuildGraph();
printf("%d",Prim(MG,MST));
}
LGraph CreateGraph2(int VertexNum)
{
LGraph Graph;
Vertex V;
Graph = (LGraph)malloc(sizeof(struct GNode));
Graph->Nv = VertexNum;
Graph->Ne = 0;
for( V = 0; V < VertexNum; V++){
Graph->G[V].FirstEdge = NULL;//注意这里的->和.的区别, ->前面应该是一个结构体变量指针, . 的前面应该是一个结构体变量
}
return Graph;
}
MGraph CreateGraph(int VertexNum)
{
MGraph Graph;//先定义一个指向图的指针
Vertex V,W;//V和W其实是表示一个顶点,并不是单纯的整数 ,虽然表示出来是一回事
Graph = (MGraph)malloc(sizeof(struct GNode));//申请图的内存空间 然后初始化
Graph->Nv = VertexNum;
Graph->Ne = 0;
for( V = 0; V < VertexNum; V++){//遍历图中的结点,令图中的结点都为0或无穷大,意为没有任何连接
for( W = 0; W < VertexNum; W++){
Graph->G[V][W] = INFINITY;/* 或者INFINITY */
}
}
return Graph;
}
void InsertEdge(MGraph Graph,Edge E)
{
/* 插入边<V1,V2> */
Graph->G[E->V1-1][E->V2-1] = E->Weight; /* 有权重的话要等于E->Weight*/
Graph->G[E->V2-1][E->V1-1] = E->Weight;
}
/* 插入边 */
void InsertEdge2(LGraph Graph,Edge E)
{
PtrToAdjVNode NewNode;
NewNode = (PtrToAdjVNode)malloc(sizeof(struct AdjVNode));
NewNode->AdjV = E->V2;
NewNode->Weight = E->Weight;
NewNode->Next = Graph->G[E->V1].FirstEdge;
Graph->G[E->V1].FirstEdge = NewNode;
}
MGraph BuildGraph()
{
MGraph Graph;
Vertex V;
Edge E;
int Nv,i;
scanf("%d",&Nv);//先输入顶点数
Graph = CreateGraph(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);
InsertEdge(Graph,E);
}
}
/* 如果有数据的话,读入数据 */
// for( V = 0; V < Graph->Nv; V++){
// scanf("%d",&(Graph->Data[V]));
// }
return Graph;
}
/* 邻接矩阵存储 - Prim最小生成树算法 */
Vertex FindMinDist( MGraph Graph, WeightType dist[] )
{ /* 返回未被收录顶点中dist最小者 */
Vertex MinV, V;
WeightType MinDist = INFINITY;
for (V=0; V<Graph->Nv; V++) {
if ( dist[V]!=0 && dist[V]<MinDist) {
/* 若V未被收录,且dist[V]更小 */
MinDist = dist[V]; /* 更新最小距离 */
MinV = V; /* 更新对应顶点 */
}
}
if (MinDist < INFINITY) /* 若找到最小dist */
return MinV; /* 返回对应的顶点下标 */
else return ERROR; /* 若这样的顶点不存在,返回-1作为标记 */
}
int Prim( MGraph Graph, LGraph MST )
{ /* 将最小生成树保存为邻接表存储的图MST,返回最小权重和 */
WeightType dist[MaxVertexNum], TotalWeight;
Vertex parent[MaxVertexNum], V, W;
int VCount;
Edge E;
/* 初始化。默认初始点下标是0 */
for (V=0; V<Graph->Nv; V++) {
/* 这里假设若V到W没有直接的边,则Graph->G[V][W]定义为INFINITY */
dist[V] = Graph->G[0][V];
parent[V] = 0; /* 暂且定义所有顶点的父结点都是初始点0 */
}
TotalWeight = 0; /* 初始化权重和 */
VCount = 0; /* 初始化收录的顶点数 */
/* 创建包含所有顶点但没有边的图。注意用邻接表版本 */
MST = CreateGraph2(Graph->Nv);
E = (Edge)malloc( sizeof(struct ENode) ); /* 建立空的边结点 */
/* 将初始点0收录进MST */
dist[0] = 0;
VCount ++;
parent[0] = -1; /* 当前树根是0 */
while (1) {
V = FindMinDist( Graph, dist );
/* V = 未被收录顶点中dist最小者 */
if ( V==ERROR ) /* 若这样的V不存在 */
break; /* 算法结束 */
/* 将V及相应的边<parent[V], V>收录进MST */
E->V1 = parent[V];
E->V2 = V;
E->Weight = dist[V];
InsertEdge2( MST, E );
TotalWeight += dist[V];
dist[V] = 0;
VCount++;
for( W=0; W<Graph->Nv; W++ ) /* 对图中的每个顶点W */
if ( dist[W]!=0 && Graph->G[V][W]<INFINITY ) {
/* 若W是V的邻接点并且未被收录 */
if ( Graph->G[V][W] < dist[W] ) {
/* 若收录V使得dist[W]变小 */
dist[W] = Graph->G[V][W]; /* 更新dist[W] */
parent[W] = V; /* 更新树 */
}
}
} /* while结束*/
if ( VCount < Graph->Nv ) /* MST中收的顶点不到|V|个 */
TotalWeight = ERROR;
return TotalWeight; /* 算法执行完毕,返回最小权重和或错误标记 */
}
题目分析:这是一道求图的最小生成树的题目,考虑用普利姆算法或克鲁斯卡尔算法。这道题中,由于要不断读取权值,初始关于村的图 采用邻接矩阵的方式存储,而最小生成树采用邻接表的方式存储。
总结:这道题目不难 ,但是有些长和复杂。需要我们掌握图的存储方式,求最小生成树的算法。
题目的坑:1.注意题目中提到的范围,宏定义的范围一定要设好,不能出现越界!
2.题目中的城市是从1开始的:城市1、2、3......依次类推 但是通常我们邻接矩阵存储的下标是从0开始的,所以插入边的时候考虑清楚(如果从0开始需要减1)!!!
相关代码放在这里了: