1、题目
id:250】【20分】B. 村村通工程(Prim算法)
时间限制1s
内存限制128MB
题目描述
"村村通"是国家一个系统工程,其包涵有:公路、电力、生活和饮用水、电话网、有线电视网、互联网等等。
村村通公路工程,是国家为构建和谐社会,支持新农村建设的一项重大举措,是一项民心工程。又称“五年千亿元”工程
该工程是指中国力争在5年时间实现所有村庄通沥青路或水泥路,以打破农村经济发展的交通瓶颈,解决9亿农民的出行难题。
现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本。
要求用Prim算法求解
输入
第1行:顶点数n
第2行:n个顶点编号
第3行:边数m
接着m行:m条边信息,格式为:顶点1 顶点2 权值
最后一行:Prim算法的起点v
输出
第1行:输出最小生成树的权值之和
接着n-1行对应n-1条边信息
按树的生长顺序输出
样例查看模式
正常显示查看格式
输入样例1 <-复制
6
v1 v2 v3 v4 v5 v6
10
v1 v2 6
v1 v3 1
v1 v4 5
v2 v3 5
v2 v5 3
v3 v4 5
v3 v5 6
v3 v6 4
v4 v6 2
v5 v6 6
v1
输出样例1
15
v1 v3 1
v3 v6 4
v6 v4 2
v3 v2 5
v2 v5 3
2、算法重点【本人理解】
我是参考B站懒猫老师的视频自己写的。
集合U为已经被选择的节点 集合V-U表示未被选择的节点
这里需要注意的是最开始集合U中的第一个元素是起点,也就是样例中的V1.这对后面的循环次数有影响!
该算法最重要的是需要每次选出V-U的元素中权值最小的节点 ,将该节点设置为已经被访问
注:当权值为0时代表该节点已经被访问,将该节点添加到集合U
struct primNode {
int adjvex; // 边的序号
int lowcost; // 权值
};
这里需要创建结构体数组shortEdge[n]
这里我写了一个寻找结构体数组中未被选择的节点中权值最小的节点的函数
int getMin(primNode *shortEdge, int n) { int i, j; for (i = 0; i < n; i++) { if (shortEdge[i].lowcost != 0) break; } for (j = 0; j < n; j++) { if (shortEdge[j].lowcost != 0 && shortEdge[i].lowcost > shortEdge[j].lowcost) i = j; } return i; }
下面是解决代码:
i、类实现
class Graph {
private:
string Vertex[MaxVertex];
int arc[MaxVertex][MaxVertex];
primNode *shortEdge;
int VertexNum, arcNum; // 节点数 边数
int Sum = 0; // 最后耗费的权重
public:
Graph(string *v, int n, int e); // 初始化节点
void setArc(); // 创建邻接矩阵
void prime(string startString); // 解决方法
};
ii、初始化如下:
Graph::Graph(std::string *v, int n, int e) {
this->VertexNum = n;
this->arcNum = e;
for (int i = 0; i < VertexNum; ++i) {
Vertex[i] = v[i];
}
this->shortEdge = new primNode[n];
}
void Graph::setArc() {
for (int i = 0; i < VertexNum; ++i) {
for (int j = 0; j < VertexNum; ++j) {
if (i == j) arc[i][j] = 0; // 自己到自己权值为0
else arc[i][j] = 2147483647; // 我将没有连接的两个节点之间设置为无穷大
}
}
string temp1, temp2;
int weight, x, y;
for (int i = 0; i < arcNum; ++i) {
cin >> temp1 >> temp2 >> weight;
for (x = 0; x < VertexNum; x++) { if (Vertex[x] == temp1) break; }
for (y = 0; y < VertexNum; y++) { if (Vertex[y] == temp2) break; }
arc[x][y] = weight, arc[y][x] = weight;
}
}
ii、最重要的代码如下:
void Graph::prime(std::string startString) { int Weight[100],index[100][2]; int times=0; // 遍历找到起点 int start = 0; for (start = 0; start < VertexNum; start++) if (Vertex[start] == startString) break; // 初始化将shortEdge结构体 for (int i = 0; i < VertexNum; i++) { shortEdge[i].adjvex = start; shortEdge[i].lowcost = arc[start][i]; } int i, j; for (i = 0; i < VertexNum - 1; i++) { // 初始集合U已经放入元素Vertex[start] j = getMin(shortEdge, VertexNum); Weight[times] = shortEdge[j].lowcost; index[times][0] = shortEdge[j].adjvex; index[times][1] = j; times++; //cout << Vertex[shortEdge[j].adjvex] << " " << Vertex[j] <<" "<< shortEdge[j].lowcost << endl; Sum += shortEdge[j].lowcost; shortEdge[j].lowcost = 0; // 将元素添加到集合U // 更新shortEdge权值 start = j; for (int x = 0; x < VertexNum; x++) { if (shortEdge[x].lowcost != 0 && arc[start][x] < shortEdge[x].lowcost) { shortEdge[x].lowcost = arc[start][x]; shortEdge[x].adjvex = start; } } } cout << Sum <<endl; for(int n = 0;n < times;n++) cout << Vertex[index[n][0]] <<" "<<Vertex[index[n][1]] <<" " << Weight[n] <<endl; }
3、AC的全部代码贴一个:
/* * 普利姆算法 * 1、首先找到起始点 例如:V0-> 0 * 2、遍历邻接矩阵第0行, 将他们填入primeNode结构体数组中 注意 将结构体数组[0] = 0 * 3、遍历!= 0的结构体数组,找到最小值, 将他的权值赋值为0,及该节点进入集合U * 4、再次遍历权值为0的结构体下标,比较更新后的U到达其他节点的权值,找到权值最小的点,将该节点的邻接边进行更新,同时将权值赋值为0 * 5、重复以3、4步骤 直到结构体数组的所有权值为0. * 该算法结束 * */ #include "iostream" using namespace std; const int MaxVertex = 100; struct primNode { int adjvex; // 边的序号 int lowcost; // 权值 }; int getMin(primNode *shortEdge, int n) { int i, j; for (i = 0; i < n; i++) { if (shortEdge[i].lowcost != 0) break; } for (j = 0; j < n; j++) { if (shortEdge[j].lowcost != 0 && shortEdge[i].lowcost > shortEdge[j].lowcost) i = j; } return i; } class Graph { private: string Vertex[MaxVertex]; int arc[MaxVertex][MaxVertex]; primNode *shortEdge; int VertexNum, arcNum; int Sum = 0; public: Graph(string *v, int n, int e); void setArc(); void prime(string startString); }; Graph::Graph(std::string *v, int n, int e) { this->VertexNum = n; this->arcNum = e; for (int i = 0; i < VertexNum; ++i) { Vertex[i] = v[i]; } this->shortEdge = new primNode[n]; } void Graph::setArc() { for (int i = 0; i < VertexNum; ++i) { for (int j = 0; j < VertexNum; ++j) { if (i == j) arc[i][j] = 0; else arc[i][j] = 2147483647; } } string temp1, temp2; int weight, x, y; for (int i = 0; i < arcNum; ++i) { cin >> temp1 >> temp2 >> weight; for (x = 0; x < VertexNum; x++) { if (Vertex[x] == temp1) break; } for (y = 0; y < VertexNum; y++) { if (Vertex[y] == temp2) break; } arc[x][y] = weight, arc[y][x] = weight; } } void Graph::prime(std::string startString) { int Weight[100],index[100][2]; int times=0; // 遍历找到起点 int start = 0; for (start = 0; start < VertexNum; start++) if (Vertex[start] == startString) break; // 初始化将shortEdge结构体 for (int i = 0; i < VertexNum; i++) { shortEdge[i].adjvex = start; shortEdge[i].lowcost = arc[start][i]; } int i, j; for (i = 0; i < VertexNum - 1; i++) { // 初始集合U已经放入元素Vertex[start] j = getMin(shortEdge, VertexNum); Weight[times] = shortEdge[j].lowcost; index[times][0] = shortEdge[j].adjvex; index[times][1] = j; times++; //cout << Vertex[shortEdge[j].adjvex] << " " << Vertex[j] <<" "<< shortEdge[j].lowcost << endl; Sum += shortEdge[j].lowcost; shortEdge[j].lowcost = 0; // 将元素添加到集合U // 更新shortEdge权值 start = j; for (int x = 0; x < VertexNum; x++) { if (shortEdge[x].lowcost != 0 && arc[start][x] < shortEdge[x].lowcost) { shortEdge[x].lowcost = arc[start][x]; shortEdge[x].adjvex = start; } } } cout << Sum <<endl; for(int n = 0;n < times;n++) cout << Vertex[index[n][0]] <<" "<<Vertex[index[n][1]] <<" " << Weight[n] <<endl; } int main() { int n, e; cin >> n; string *v = new string[n]; for (int i = 0; i < n; i++)cin >> v[i]; cin >> e; Graph *graph = new Graph(v, n, e); graph->setArc(); string startstr; cin >> startstr; graph->prime(startstr); return 0; }