最小生成树
一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边
Prim算法
普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)),且其所有边的权值之和亦为最小。该算法于1930年由捷克数学家沃伊捷赫·亚尔尼克(英语:Vojtěch Jarník)发现;并在1957年由美国计算机科学家罗伯特·普里姆(英语:Robert C. Prim)独立发现;1959年,艾兹格·迪科斯彻再次发现了该算法。因此,在某些场合,普里姆算法又被称为DJP算法、亚尔尼克算法或普里姆-亚尔尼克算法。
流程表格
图例 | 解释 | 集合 |
---|---|---|
初始图 | {} | |
顶点D被任意选为起始点。顶点A、B、E和F通过单条边与D相连。A是距离D最近的顶点,因此将A及对应边AD以高亮表示。 | {D} | |
下一个顶点为距离D或A最近的顶点。B距D为9,距A为7,E为15,F为6。因此,F距D或A最近,因此将顶点F与相应边DF以高亮表示。 | {D, A} | |
下一个顶点为距离D或A最近的顶点。B距D为9,距A为7,E为15,F为6。因此,F距D或A最近,因此将顶点F与相应边DF以高亮表示。 | {D, A, F} | |
… step 5 6 7 | 以此类推 | {…} |
顶点G是唯一剩下的顶点,它距F为11,距E为9,E最近,故高亮表示G及相应边EG。 | {D, A, F, B, E, C } | |
现在,所有顶点均已被选取,图中绿色部分即为连通图的最小生成树。在此例中,最小生成树的权值之和为39。 | {D, A, F, B, E, C, G } |
如何求最短路径
- 输入一个图
- 维护一个lowcost[v]的数组,是当前已知节点到其他所有节点的最短距离。
- 每次从lowcost中找到最短的(不能添加已经添加过的节点),然后添加到已知节点里,然后更新lowcost
- 重复直到所有节点都已知。
Prim代码实现
//
// main.cpp
// HiHocoder
//
// Created by Alps on 16/5/9.
// Copyright © 2016年 chen. All rights reserved.
//
#include <iostream>
#include <cstring>
#include <string>
#include <vector>
using namespace std;
int graph[1002][1002]; //节点最大1001个
int lowcost[1002];
int flag[1002] = {0};
int findMin(int T){
int mnLen = -1, mnNode = -1;
for(int i = 0; i < T; i++){
if(flag[i] == 0 && (mnLen == -1 || mnLen > lowcost[i])){
mnLen = lowcost[i];
mnNode = i;
}
}
if (mnNode == -1) {
return -1;
}
for(int i = 0; i < T; i++){
lowcost[i] = min(lowcost[i], graph[mnNode][i]);
}
flag[mnNode] = -1;
return mnLen;
}
long Prim(int graph[][1002], int T, int start){
flag[start] = -1;
for(int i = 0; i < T; i++){
lowcost[i] = graph[start][i];
}
long ans = 0;
while(1){
int mn = findMin(T);
if(mn == -1) break;
ans += mn;
}
return ans;
}
int main(){
int T;
cin>>T;
for(int i = 0; i < T; i++){
for(int j = 0; j < T; j++){
cin>>graph[i][j];
}
}
long len = Prim(graph, T, 0);
cout<<len<<endl;
return 0;
}
测试数据
5
0 1005 6963 392 1182
1005 0 1599 4213 1451
6963 1599 0 9780 2789
392 4213 9780 0 5236
1182 1451 2789 5236 0
输出:
4178