普利姆(Prime)算法(只與頂點相關)
算法描述:
普利姆算法求最小生成樹時候,和邊數無關,只和定點的數量相關,所以適合求稠密網的最小生成樹,時間複雜度爲O(n*n)。
算法過程:
1.將一個圖的頂點分爲兩部分,一部分是最小生成樹中的結點(A集合),另一部分是未處理的結點(B集合)。
2.首先選擇一個結點,將這個結點加入A中,然後,對集合A中的頂點遍歷,找出A中頂點關聯的邊權值最小的那個(設爲v),將此頂點從B中刪除,加入集合A中。
3.遞歸重複步驟2,直到B集合中的結點爲空,結束此過程。
4.A集合中的結點就是由Prime算法得到的最小生成樹的結點,依照步驟2的結點連接這些頂點,得到的就是這個圖的最小生成樹。
算法實現具體過程:
1.將第一個點放入最小生成樹的集合中(標記visit[i]=1意思就是最小生成樹集合)。
2.從第二個點開始,初始化lowcost[i]爲跟1點相連(僅僅相連)的邊的權值(lowcost[i]不是這個點的最小權值!在以後會逐步更新)。
3.找最小權值的邊。
從第二點開始遍歷,如果不是最小生成樹的集合的點,則找出從2到n的最小權值(lowcost[j])。
4.將找出來的最小權值的邊的頂點加入最小生成樹的集合中(標記visit[i] = 1),權值想加。
5.更新lowcost[j]集合。
假設第一次:lowcost[2]代表與1相連的點的權值,現在加入了k點。則比較k點與2點的邊map[k][2]和lowcost[2]的大小,若lowcost[2]大,則lowcost[2] = map[k][2]。(關鍵步驟:實質就是每在最小生成樹集合中加入一個點就需要把這個點與集合外的點比較,不斷的尋找兩個集合之間最小的邊)
6.循環上述步驟,指導將全部頂點加入到最小生成樹集合爲止。
代碼如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define MAXN 110
int map[MAXN][MAXN], lowcost[MAXN];
bool visit[MAXN];
int nodenum, sum;
void prim()
{
int temp, k;
sum = 0;
memset(visit, false, sizeof(visit)); //初始化visit
visit[1] = true;
for(int i = 1; i <= nodenum; ++i) //初始化lowcost[i]
lowcost[i] = map[1][i];
for(int i = 1; i <= nodenum; ++i)//找生成樹集合點集相連最小權值的邊
{
temp = INF;
for(int j = 1; j <= nodenum; ++j)
if(!visit[j] && temp > lowcost[j])
temp = lowcost[k = j];
if(temp == INF) break;
visit[k] = true; //加入最小生成樹集合
sum += temp;//記錄權值之和
for(int j = 1; j <= nodenum; ++j) //更新lowcost數組
if(!visit[j] && lowcost[j] > map[k][j])
lowcost[j] = map[k][j];
}
}
int main()
{
int a, b, cost, edgenum;
while(scanf("%d", &nodenum) && nodenum)
{
memset(map, INF, sizeof(map));
edgenum = nodenum * (nodenum - 1) / 2;
for(int i = 1; i <= edgenum; ++i) //輸入邊的信息
{
scanf("%d%d%d", &a, &b, &cost);
if(cost < map[a][b])
map[a][b] = map[b][a] = cost;
}
prim();
printf("%d\n", sum); //最小生成樹權值之和
}
return 0;
}