1. 程式人生 > >ACM_他和她(最大生成樹+最短路徑)

ACM_他和她(最大生成樹+最短路徑)

遇到 否則 ret 所有 end 接下來 這樣的 機房 那種

他和她

Time Limit: 2000/1000ms (Java/Others)

Problem Description:

大二上學期剛過完,平時成績不錯的小V參加了一個小型編程比賽,遇到一道題,雖然是書上的卻不會做。於是找了在機房的他幫忙。
他:什麽題目這麽厲害,書上有卻不會?
小V:就是給若幹個城市編號,也告訴了我連接這些城市的公路的距離,也有一些城市間是沒有直接的路的。求從一個城市到另外一個城市的最短距離。
他:這不很簡單的單向最短路徑麽?照書敲都行啦。
小V:不是什麽比賽都能帶書。
他:那你自己敲出來還不行麽?
小V:不會敲..完全下不了手,我只會考試那種畫圖的,寫代碼的話連思路都沒有。
他:好吧- -!思路是這樣的:
    s為起點,w[u,v]為點u和v之間的邊的長度(無邊則長度設為一個很大的數,只要比w[]的最大值大就行),把s點到其他點的距離保存在一個數組裏,假設是dis[]。
    1.初始化:起點到起點的距離dis[s]設為0,s點到其他點(v)的距離設w[s,v],同時把所有的點都標記為未訪問過,s點標記訪問過。
    2.循環n-1次:
    ①在沒有訪問過的點中取dis[]距離最小且未訪問的點u,並標記為已訪問。
    ②對於每個與u相鄰的點v,執行Relax(u,v),也就是說,如果滿足u點在最短路徑上的距離+u到v點距離小於原本v點到最短路徑上的距離就把v點到最短路徑的距離更新成更短的距離。
    3.結束。此時對於任意的u,dis[u]就是s到u的距離。
小V:標記?
他:開個數組表示就行了。
小V:那我試敲下。
他:等會,順便先判斷下這些城市能不能從任意一個城市i到另外一個城市j吧。
小V:生成樹?
他:這麽聰明?不過判斷連通不一定要生成樹,你還可以用搜索、並查集,搜索就看能不能一次就搜遍,集合就看是不是它們在同一連通分量,不是就把它們合並在一個集合裏,最終判斷是不是全部點同屬一個集合就行。
小V:那個叫什麽並查集的怎麽聽起來跟克魯斯卡爾算法那麽像。
他:kruskal是可以用並查集優化的。不過既然你這麽說了,那就幹脆也求個最小生成樹的值吧,即用公路長度和最小的n-1個公路將n個城市連在一起,求這n-1條路的公路長度。
小V:那代碼上跟最短路那個算法有什麽區別?
他:改下Relax()的條件,每加入一個點u,看其他跟該點有邊的點v,v到樹上的距離是不是小於u到樹上源點的距離,是就更新,再把生成樹上那些點的權值加起來就是答案。
大概半小時後,小V敲得差不多了。
他:看你都敲這麽多了,也告訴你這麽多了,不如加個難度,改求最大生成樹,這樣,我給你4個例子,你對照下答案。
經過比對,答案還是不對。這時他喵了一眼就看出是初始化問題,但他沒有告訴她。因為他知道這是她必須跨過的坎,有多少學生能把各種算法的原理講得很流暢,卻寫不出代碼。
而對於她,他希望她能解決的不只是這個問題,因為這樣才能實現他的心願:一!起!刷!題!

Hints:不要輕易放棄,水題雖多,AC不易。

Input:

多組數據,每組數據第一行是兩個數N,M(1<=N<=100,0<=M<=1000),N表示城市個數,城市從1開始編號,M表示公路數目,編號為1的城市為起點,編號為N的為終點,接下來有M行,每行兩個整數A,B,C(1<=A,B<=N,1<=C<=1000),表示有一條長度為C的公路連接AB兩城市。數據保證1號城市不是孤立的。

Output:

如果從1號城市出發,不能到其他所有城市,則輸出-1,否則按樣例格式輸出最大生成樹的值和從1號城市到N號城市的最短距離。

Sample Input:

3 3
1 2 45
1 3 20
3 1 10
3 1
1 2 5
2 2
2 1 250
1 2 520
4 7
1 4 88
2 4 1
4 2 50
3 1 6
4 1 1000
4 3 30
2 3 100

Sample Output:

spanning tree:65 shortest distance:10
-1
spanning tree:520 shortest distance:250
spanning tree:1150 shortest distance:36
解題思路:最大生成樹即將prim算法中每次找最小權值改為找最大權值,每加入一個點更新一下用最大權值代替原來的最小值即可。這題還要考慮重邊的情況,簡單處理,簡單水過。
AC代碼:
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=105;
4 const int INF=0x3f3f3f3f; 5 int n,m,a,b,c,maxcost[maxn],dist[maxn],G[maxn][maxn],cost[maxn][maxn];bool vis[maxn],used[maxn]; 6 int prim(){//最大生成樹 7 for(int i=1;i<=n;++i)maxcost[i]=G[1][i]; 8 maxcost[1]=0;used[1]=true; 9 int res=0; 10 for(int i=1;i<n;++i){ 11 int k=-1; 12 for(int j=1;j<=n;++j) 13 if(!used[j]&&(k==-1||maxcost[k]<maxcost[j]))k=j;//每次找最大邊權 14 if(k==-1)break; 15 used[k]=true;res+=maxcost[k]; 16 for(int j=1;j<=n;++j) 17 if(!used[j])maxcost[j]=max(maxcost[j],G[k][j]);//更新周圍點到已經歸納點的集合的最小權值 18 } 19 return res; 20 } 21 int dijkstra(){//最短路徑 22 for(int i=1;i<=n;++i)dist[i]=cost[1][i]; 23 dist[1]=0;vis[1]=true; 24 for(int i=1;i<n;++i){ 25 int k=-1; 26 for(int j=1;j<=n;++j) 27 if(!vis[j]&&(k==-1||dist[k]>dist[j]))k=j; 28 if(k==-1)break; 29 vis[k]=true; 30 for(int j=1;j<=n;++j) 31 if(!vis[j])dist[j]=min(dist[j],dist[k]+cost[k][j]); 32 } 33 return dist[n]; 34 } 35 int main(){ 36 while(~scanf("%d%d",&n,&m)){ 37 memset(vis,false,sizeof(vis));//全部初始化為未訪問狀態即false 38 memset(used,false,sizeof(used)); 39 for(int i=1;i<=n;++i){ 40 for(int j=1;j<=n;++j){ 41 G[i][j]=(i==j?0:-INF);//求最大生成樹:初始化為最小值-INF 42 cost[i][j]=(i==j?0:INF);//求最短路徑:初始化為最大值INF 43 } 44 } 45 for(int i=1;i<=m;++i){ 46 scanf("%d%d%d",&a,&b,&c); 47 G[a][b]=G[b][a]=max(G[a][b],c);//去重,代替最小權值 48 cost[a][b]=cost[b][a]=min(cost[a][b],c);//去重,代替最大權值 49 } 50 int ok=prim(); 51 if(ok<=0)cout<<-1<<endl;//如果不大於0,說明不能從1到達n,直接輸出-1 52 else cout<<"spanning tree:"<<ok<<" shortest distance:"<<dijkstra()<<endl; 53 } 54 return 0; 55 }

ACM_他和她(最大生成樹+最短路徑)