题目描述
思路分析
本题要求每个村落都有公路连通,且成本最低。它的实质是求最小生成树
由题目描述可知,构成的图为稠密图,因此采用Prim算法
值得注意的地方
当边数Ne < 顶点数Nv - 1时,此时是一个非连通图,是无法构成最小生成树的,因此对这种情况单独进行判断
我在解题时遇到的一个问题是,如果我在调用buildGraph()
创建好图后,再来判定是否有Ne < Nv - 1
,此时我是无法得出正确答案的(但我不知道这是为什么…sad
因此,我将该判断语句放在了buildGraph()
函数内部执行,根据buildGraph()
的返回值来判断是否连通。
代码展示
/*
本题要求每个村落都有公路连通,且成本最低。它的实质是求最小生成树
本题先采用Prim算法:使用稠密图,类似于Dijkstra算法
*/
#include <cstdlib>
#include <iostream>
#define MAXSIZE 1000
#define INFINITY 65535
#define ERROR 65535
typedef int vertex;
typedef int weightType;
/* 边节点 */
struct ENode
{
vertex V1, V2;
weightType cost;
};
typedef ENode* ptrToENode;
typedef ptrToENode Edge;
/* 图 */
struct GNode
{
int Ne; /* 边数 */
int Nv; /* 顶点数 */
weightType G[MAXSIZE][MAXSIZE]; /* 数组下标比实际编号小1 */
};
typedef GNode* ptrToGNode;
typedef ptrToGNode MGraph;
/* 插入边 */
void insertEdge(MGraph Graph, Edge E)
{ /* 数组下标比实际编号小1 */
Graph->G[E->V1 - 1][E->V2 - 1] = E->cost;
Graph->G[E->V2 - 1][E->V1 - 1] = E->cost;
}
/* 创建并初始化图 */
MGraph creatGraph(int Nv)
{
MGraph Graph = (MGraph)malloc(sizeof(GNode));
Graph->Nv = Nv;
Graph->Ne = 0;
for (vertex V = 0; V < Graph->Nv; V++)
{
for (vertex W = 0; W < Graph->Nv; W++)
{
Graph->G[V][W] = INFINITY;
}
}
return Graph;
}
/* 构造完整的图 */
MGraph buildGraph()
{
int Nv, Ne;
std::cin >> Nv >> Ne;
if (Ne < Nv - 1)
return NULL;
MGraph Graph = creatGraph(Nv);
Graph->Ne = Ne;
/* 读取并插入边 */
Edge E = (Edge)malloc(sizeof(ENode));
for (int i = 0; i < Graph->Ne; i++)
{
std::cin >> E->V1 >> E->V2 >> E->cost;
insertEdge(Graph, E);
}
return Graph;
}
vertex getMinDist(MGraph Graph, int dist[])
{
vertex minV;
int minDist = INFINITY;
for (vertex V = 0; V < Graph->Nv; V++)
{ /* 还未被收录 */
if (dist[V] != 0)
{
if (dist[V] < minDist)
{
minV = V;
minDist = dist[V];
}
}
}
if (minDist != INFINITY)
return minV;
else return ERROR;
}
bool Prim(MGraph Graph, int dist[], int parent[], vertex S)
{
vertex V, W;
int count = 0; /* 计数收入树中的顶点个数 */
/* 初始化 */
for(V = 0; V < Graph->Nv; V++)
{
dist[V] = Graph->G[S][V];
if (dist[V] == INFINITY)
parent[V] = -1;
else
parent[V] = S;
}
/* 将源点收入树中 */
dist[S] = 0;
parent[S] = -1;
count++;
while (1)
{
V = getMinDist(Graph, dist);
if (V == ERROR)
break;
/* 将V收录进树中 */
dist[V] = 0;
count++;
/* 遍历V的所有邻接点 */
for (W = 0; W < Graph->Nv; W++)
{
if (Graph->G[V][W] != INFINITY)
{
if (dist[V] + Graph->G[V][W] < dist[W])
{
dist[W] = dist[V] + Graph->G[V][W];
parent[W] = V;
}
}
}
}
/* 当结束while循环会有两种情况:1、已经实现最小生成树 2、最小生成树不连通 */
if (count < Graph->Nv)
{
return false;
}
return true;
}
int main()
{
int dist[MAXSIZE];
int parent[MAXSIZE];
int sum = 0;
vertex S, V, W;
MGraph Graph = buildGraph();
/* 先判断能否满足最小生成树顶点和边的关系 */
if(Graph == NULL)
{
std::cout << "-1";
return 0;
}
/* 利用Prim求最小生成树,此处令村庄1作为树的根节点 */
S = 0;
if (!Prim(Graph, dist, parent, S))
{
std::cout << "-1";
return 0;
}
/* 求最低成本 */
for (V = 0; V < Graph->Nv; V++)
{
if(V != S)
{
W = parent[V];
sum += Graph->G[V][W];
}
}
std::cout << sum;
return 0;
}
心路历程
对于dist的初始化真的傻傻分不清,什么情况初始化为什么值了解得还不够透彻…