|
Posted on 2009-08-28 09:12 reiks 閱讀(534) 評論(0) 編輯 收藏 引用 所屬分類: 算法與數據結構
// PRIM(簡單版) 最小生成樹算法 (Minimum Spanning Tree)
// 輸入:圖g; // 有向圖或者無向圖
// 輸出:(1)最小生成樹長sum;
// (2)最小生成樹prev。
// 結構: 圖g用鄰接矩陣表示,最短邊長dist用數組表示。
// 算法:Prim算法
//復雜度:O(|V|^2)
#include <iostream>
#include <vector>
#include <list>
#include <iterator>
#include <algorithm>
#include <numeric>
#include <functional>
#include <climits>
using namespace std;

int n; // n : 頂點個數
vector<vector<int> > g; // g : 圖(graph)(用鄰接矩陣(adjacent matrix)表示)
vector<bool> known; // known : 各點是否已經選取
vector<int> dist; // dist : 已選取點集到未選取點的最小邊長
vector<int> prev; // prev : 最小生成樹中各點的前一頂點
int s; // s : 起點(start)
int sum; // sum : 最小生成樹長

bool Prim() // 貪心算法(Greedy Algorithm)
  {
known.assign(n, false);
dist.assign(n, INT_MAX);
prev.resize(n); // 初始化known、dist、prev。
dist[s] = 0; // 初始化起點到自身的路徑長為0。
int i;
for (i = 0; i < n; ++i)
 {
int min = INT_MAX, v;
for (int i = 0; i < n; ++i)
if (!known[i] && min > dist[i])
min = dist[i], v = i; // 尋找未知的最短路徑長的頂點v,
if (min == INT_MAX) break; // 如果找不到,退出;
known[v] = true; // 如果找到,將頂點v設為已知,
sum += dist[v]; // 調整最小生成樹長
for (int w = 0; w < n; ++w) // 遍歷所有v指向的頂點w,
if (!known[w] && g[v][w] < INT_MAX && dist[w] > g[v][w])
dist[w] = g[v][w], prev[w] = v; // 調整頂點w的最短路徑長dist和最短路徑的前一頂點 prev。
}
return i == n; // 如果選取頂點個數為n,成功。
}

int main()
  {
n = 7;
g.assign(n, vector<int>(n, INT_MAX));
g[0][1] = g[1][0] = 2; g[0][2] = g[2][0] = 4; g[0][3] = g[3][0] = 1;
g[1][3] = g[3][1] = 3; g[1][4] = g[4][1] = 10;
g[2][3] = g[3][2] = 2; g[2][5] = g[5][2] = 5;
g[3][4] = g[4][3] = 7; g[3][5] = g[5][3] = 8; g[3][6] = g[6][3] = 4;
g[4][6] = g[6][4] = 6;
g[5][6] = g[6][5] = 1;
s = 0; // 起點任選
sum = 0;
if (Prim())
 {
cout << sum << endl;
for (int i = 1; i < n; ++i)
if(i != s) cout << prev[i] << "->" << i << endl;
}
else
 {
cout << "Some vertex cann't be reached." << endl;
}
system("pause");
return 0;
}

 /**//*=============================*/
 /**//*
Write By LiveStar (2008.07.23)
*/
#include <stdio.h>
//max表示點,邊的最大個數
#define max 1000
//wq表示兩點的距離為無窮
#define wq 9999
//鄰接矩陣存儲相應的邊的權重
int mix[max][max];

//輸入并構造鄰接矩陣
void input(int n,int m)
  {
int i,j,sp,ep,wg;
//初始化鄰接矩陣每個都是無窮大
for (i=0;i<n;i++)
for (j=0;j<n;j++)
mix[i][j]=wq;
printf("請輸入邊的起點、終點、權重(EX:1 2 3):\n");
for (i=0;i<m;i++)
 {
scanf("%d %d %d",&sp,&ep,&wg);
//無向連通圖
mix[sp][ep]=wg;
mix[ep][sp]=wg;
}
}

//顯示鄰接矩陣
void output(int n,int m)
  {
int i,j;
printf("\n鄰接矩陣如下:\n\n");
for (i=0;i<n;i++)
 {
for (j=0;j<n;j++)
printf("%d\t",mix[i][j]);
printf("\n");
}
}

//prim算法實現
void prim(int n,int r)
  {
//a[max]用來存放各個點到已經標記點的集合的最短距離
int a[max];
//b[max]用來記錄每個點的標記狀態,false表示還沒標記。
bool b[max];
int i,j,k,min;
//初始化從根節點開始
for (i=0;i<n;i++)
 {
a[i]=mix[r][i];
b[i]=false;
}
b[r]=true;
printf("\n依次被標記的點及相應邊的權重:\n");
printf("%d\t%d\n",r,0);
for (i=0;i<n-1;i++)//還剩n-1個點
 {
k=0;
min=wq;
for (j=0;j<n;j++)
 {
//第j個點到已經標記點的集合的距離最小且這個點還沒有被標記
//k記錄下一個將被標記的點
if (a[j]<min&&b[j]==false)
 {
min=a[j];
k=j;
}
}
b[k]=true;//標記節點k
printf("%d\t%d\n",k,min);
//更新a[]里的狀態,時刻保持最新的狀態
//存放各個點到已經標記點的集合的最短距離
for (j=0;j<n;j++)
 {
if (mix[k][j]<a[j])
 {
a[j]=mix[k][j];
}
}
}
}

int main()
  {
//n是點的個數 ,m是邊的個數
int n,m,r;
printf("請輸入點的個數和邊的個數(EX:1 3):\n");
scanf("%d %d",&n,&m);
//輸入并構造鄰接矩陣
input(n,m);
//顯示鄰接矩陣
output(n,m);
while (1)
 {
printf("\n請輸入根節點(-1跳出):\n");
scanf("%d",&r);
if(r==-1)
break;
//prim算法實現
prim(n,r);
printf("\n\n");
}
return 0;
}
|