題目連結:http://poj.org/problem?id=2485
題意:一個地方F,沒有Highways,交通不便,要建 Highways,每個Highway串連兩個城鎮,所有的Highways都是直線的。
範例輸入意思:
T 案例數
N 城鎮數
下面N行N列,以矩陣的形式
v1 v2 v3
v1 0 990 692
v2 990 0 179
v3 692 179 0
範例輸出意思:
明顯路徑是 v1-v3-v2,路徑中最大的數是692,輸出的就是它
思路:
最小產生樹問題,一般用prim,kuskal演算法 。prim演算法是以頂點來擴充的。把每次找到的頂點加入頂點集U,然後下次再找與U相鄰且最小的頂點,加入U,依次,得到最小產生樹。
參考:http://en.wikipedia.org/wiki/Prim's_algorithm
#include <stdio.h>#include <string.h>#define M 502#define INF 99999int prim[M][M];int visit[M];int Len[M]; //Len[i] 記錄頂點集U到i的最短距離,注意區別dijkstra中的dis[i]int n;int ans;int prim_solve(int xx){ int minx; int i,j,k; memset(visit,0,sizeof(visit)); //開始時都未訪問 ans = -1; for (i = 1; i <= n; i++) { Len[i] = prim[xx][i]; } Len[xx] = 0; visit[xx] = 1; //此時U中只有起點xx for(i = 1; i< n; i++) // 注意:不能=,因為xx起點已經訪問過,所以只需再訪問n-1個 { minx = INF; for(j = 1; j <= n; j++ ) //很像dijkstra中的吧,但注意:這裡的Len[i]與dijkstra中dis[i]意義相當不同 { //這裡找的是:與頂點集U相鄰的距離最小值 if ( !visit[j] && Len[j] < minx) { minx = Len[j]; k = j; } } visit[k] = 1; //找到,加入U if (ans < minx) //儲存最短路徑中最大的一條邊,比如範例中692 { ans = minx; } //i=1時,U中只有起點xx和新加入的k,Len[j]與prim[j]比較:就是比較xx到j的距離和新加入U的k頂點到j的距離 //之後,Len[j]就是U到j的最短距離啦,這樣把U中所有頂點看成一個,Len[j]就是U到j(V-U中任意一個)的最短距離 //以此類推,i>1 時,每次都把原來的頂點集U到j的距離和新加入的k到j的距離比較,這樣得到了新U到j的最短距離 //從而,就得到了新U到V-U中任一頂點的距離,儲存在 Len中 for (j = 1; j <= n; j++) { if ( !visit[j] && Len[j] > prim[k][j]) { Len[j] = prim[k][j]; } } } return ans;}int main(){ int T; int i,j; scanf("%d",&T); while(T--) { memset(prim,0,sizeof(prim)); scanf("%d",&n); for(i = 1; i <= n; i++) for(j = 1; j <= n; j ++) { scanf("%d",&prim[i][j]); } printf("%d\n",prim_solve(1)); //以第一個頂點開始,也可以是其他,無所謂,改成2。。。。一樣 } return 0;}
你會覺得prim代碼實現是來很像dijkstra,參考http://blog.csdn.net/bill_ming/article/details/7578037
prim是計算最小產生樹的演算法,dijkstra是計算最短路徑的演算法。
他們都是從幾何V-U中不斷選出權值最小的頂點加入U,那他們是否可以互用嗎?
必然不可以嘛!
他們的不同之處是:
prim是把U中的點看成一個整體,每次尋找V-U中和U距離最小的頂點加入U。而dijkstra是相對於源點V0而言的,每次尋找的是V-U中裡V0最近的頂點。
所以:
dijkstra中dis[i]記錄的是V0到i的最短距離。
用這樣的for迴圈來更新dis,結果dis[j]是比較源點V0直接到j 和 源點V0經過k到j的最短距離所得結果
for(j = 1; j <= n; j++)
{
if ( !visit[j] && dis[j] > dis[k] + map[k][j])
{
dis[j] = dis[k] + map[k][j];
}
}
而prim中Len[i]記錄的是頂點集U(看成整體)到i的最短距離
用這樣的for迴圈來更新頂點集U到V-U中任一頂點的距離,比較的是加入k之前頂點集到j的距離 和 k 到j的距離 。
得到的就是包含k的新頂點集U到j的最短距離。 (感謝羅康琦大牛!!!)
//i=1時,U中只有起點xx和新加入的k,Len[j]與prim[j]比較:就是比較xx到j的距離和新加入U的k頂點到j的距離
//之後,Len[j]就是U到j的最短距離啦,這樣把U中所有頂點看成一個,Len[j]就是U到j(V-U中任意一個)的最短距離
//以此類推,i>1 時,每次都把原來的頂點集U到j的距離和新加入的k到j的距離比較,這樣得到了新U到j的最短距離
//從而,就得到了新U到V-U中任一頂點的距離,儲存在 Len中
for (j = 1; j <= n; j++)
{
if ( !visit[j] && Len[j] > prim[k][j])
{
Len[j] = prim[k][j];
}
}
他們的區別就知道了。所以不能互用啊,prim還是乖乖算最小產生樹吧,dijkstra還是繼續管計算最短路徑吧
舉個例子就知道他們不能亂來了:
有四個頂點(v0,
v1, v2, v3)和四條邊且邊值定義為(v0,
v1)=20, (v0, v2)=10, (v1, v3)=2, (v3, v2)=15的圖,用Prim演算法得到的最小產生樹中v0跟v1是不直接相連的,也就是在最小產生樹中v0v1的距離是v0->v2->v3->v1的距離是27,而用Dijkstra演算法得到的v0v1的距離是20,也就是二者直接連線的長度。
SO。。。。