hdu 1233 还是畅通工程 最小生成树(prim算法 + kruskal算法)
還是暢通工程
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Time Limit: 4000/2000 MS (Java/Others)????Memory Limit: 65536/32768 K (Java/Others)Problem Description 某省調(diào)查鄉(xiāng)村交通狀況,得到的統(tǒng)計表中列出了任意兩村莊間的距離。省政府“暢通工程”的目標(biāo)是使全省任何兩個村莊間都可以實(shí)現(xiàn)公路交通(但不一定有直接的公路相連,只要能間接通過公路可達(dá)即可),并要求鋪設(shè)的公路總長度為最小。請計算最小的公路總長度。
Input 測試輸入包含若干測試用例。每個測試用例的第1行給出村莊數(shù)目N ( < 100 );隨后的N(N-1)/2行對應(yīng)村莊間的距離,每行給出一對正整數(shù),分別是兩個村莊的編號,以及此兩村莊間的距離。為簡單起見,村莊從1到N編號。
當(dāng)N為0時,輸入結(jié)束,該用例不被處理。
Output 對每個測試用例,在1行里輸出最小的公路總長度。
Sample Input 3 1 2 1 1 3 2 2 3 4 4 1 2 1 1 3 4 1 4 1 2 3 3 2 4 2 3 4 5 0
Sample Output 3 5HintHint Huge input, scanf is recommended.
?在無向帶權(quán)連通圖G中,如果一個連通子樹包含所有頂點(diǎn),并且連接這些頂點(diǎn)的邊權(quán)之和最小,
那么這個連通子圖就是G的最小生成樹。求最小生成樹的一個常見算法是Prim算法。
prim算法(時間復(fù)雜度為O(n^3)):
Prim算法的基本思想是:
1)設(shè)置兩個集合V和S,任意選擇一個頂點(diǎn)作為起始頂點(diǎn),將起始頂點(diǎn)放入集合S,其余頂點(diǎn)存入集合
V中;2)然后使用貪心策略,選擇一條長度最短并且端點(diǎn)分別在S和V中邊(即為最小生成樹的中的一條
邊),將這條邊在V中的端點(diǎn)加入到集合S中;3)循環(huán)執(zhí)行第2)步直到S中包含了所有頂點(diǎn)。
#include<stdio.h> #include<string.h> #define inf 0x3f3f3f3f int map[100][100],s[100],vis[100]; int n,m; int prim() {int i,j,t,p,min,minpos,cnt;int ans=0;cnt=0; /*記錄已經(jīng)加入的點(diǎn)的個數(shù)*/vis[1]=1; /*從第一個點(diǎn)開始找*/s[cnt++]=1; /*s數(shù)組保存已經(jīng)加入的點(diǎn)*/while(cnt<n) /*點(diǎn)還沒有加入完*/{t=cnt;min=inf;for(i=0;i<t;i++) {p=s[i];for(j=1;j<=n;j++){if(!vis[j]&&map[p][j]<min) /*在已經(jīng)加入的點(diǎn)和沒加入的點(diǎn)之間找出一條最短路,*/{min=map[p][j];minpos=j; /*記錄下新找到的最短路的端點(diǎn)*/}}}ans+=min; s[cnt++]=minpos; /*更新已經(jīng)加入的點(diǎn)*/vis[minpos]=1; }return ans; } int main() {int u,v,w,i;while(~scanf("%d",&n)&&n){m=n*(n-1)/2;memset(vis,0,sizeof(vis));memset(map,inf,sizeof(map));for(i=0;i<m;i++){scanf("%d%d%d",&u,&v,&w);map[u][v]=w;map[v][u]=w;}int sum=prim();printf("%d\n",sum);}return 0; }
上面的算法有三個循環(huán),時間復(fù)雜度為O(N^3),考慮到由于使用的是貪心策略,則每添加一個新頂點(diǎn)到集合S中的時候,才會改變V中每個點(diǎn)到S中的點(diǎn)的最小邊的長度。因此可以用一個數(shù)組nearest[N](N為頂點(diǎn)個數(shù))記錄在生成最小數(shù)的過程中,記錄V中每個點(diǎn)的到S中點(diǎn)的最小邊長,用另外一個數(shù)組adj[N]記錄使得該邊最小的對應(yīng)的鄰接點(diǎn)。那么O(N)的時間了找到最短的邊,并且能在O(N)的時間里更新nearest[N]和adj[N]。因此可以得到O(N^2)的算法。
#include<stdio.h> #include<string.h> #define inf 0x3f3f3f3f int map[100][100]; int n,m; /*記當(dāng)前生成樹的節(jié)點(diǎn)集合為S,未使用的節(jié)點(diǎn)結(jié)合為V*/ int vis[100]; //標(biāo)記某個點(diǎn)是否在S中 int adj[100]; //記錄與S中的點(diǎn)最接近的點(diǎn) int nearest[100]; //記錄V中每個點(diǎn)到S中的點(diǎn)的最短邊 int prim() {int i,j,min;int ans=0;vis[1]=1;for(i=2;i<=n;i++){nearest[i]=map[1][i]; adj[i]=1;}int cnt=n-1; /*記錄邊的條數(shù)*/while(cnt--){min=inf;j=1;for(i=1;i<=n;i++){if(!vis[i]&&nearest[i]<min){min=nearest[i];j=i;}}ans+=map[j][adj[j]];vis[j]=1;for(i=1;i<=n;i++){if(!vis[i]&&map[i][j]<nearest[i]){nearest[i]=map[i][j]; /*找最短的邊*/adj[i]=j; /*找最接近的點(diǎn)*/}}}return ans; } int main() {int i,sum,u,v,w;while(~scanf("%d",&n)&&n){memset(vis,0,sizeof(vis));memset(map,0,sizeof(map));m=n*(n-1)/2;for(i=0;i<m;i++){scanf("%d%d%d",&u,&v,&w);map[u][v]=map[v][u]=w;}sum=prim();printf("%d\n",sum);}return 0 ; }
Kruskal算法(時間復(fù)雜度O(ElogE),E為邊數(shù)):
給定無向連同帶權(quán)圖G = (V,E),V = {1,2,...,n}。Kruskal算法構(gòu)造G的最小生成樹的基本思想是:
(1)首先將G的n個頂點(diǎn)看成n個孤立的連通分支。將所有的邊按權(quán)從小大排序。
(2)從第一條邊開始,依邊權(quán)遞增的順序檢查每一條邊。并按照下述方法連接兩個不同的連通分支:當(dāng)查看到第k條邊(v,w)時,如果端點(diǎn)v和w分別是當(dāng)前兩個不同的連通分支T1和T2的端點(diǎn)是,就用邊(v,w)將T1和T2連接成一個連通分支,然后繼續(xù)查看第k+1條邊;如果端點(diǎn)v和w在當(dāng)前的同一個連通分支中,就直接再查看k+1條邊。這個過程一個進(jìn)行到只剩下一個連通分支時為止。
此時,已構(gòu)成G的一棵最小生成樹。
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; int father[100]; int n,m; struct point {int u;int v;int w; }a[5000]; bool comp(point a1,point a2) /*按權(quán)值從小到大排序*/ {return a1.w<a2.w; } void initial() /*并查集初始化*/ {for(int i=0;i<=100;i++)father[i]=i; } int find(int x) /*查找根節(jié)點(diǎn)*/ {if(father[x]==x)return x;return find(father[x]); } void merge(int p,int q) /*合并兩個集合*/ {int pp=find(p);int qq=find(q);if(pp!=qq){if(pp<qq)father[qq]=pp;elsefather[pp]=qq;} } int kruskal() {initial(); /*初始化*/int ans=0;sort(a+1,a+m+1,comp); /*排序*/for(int i=1;i<=m;i++){int x=find(a[i].u);int y=find(a[i].v);if(x!=y) /*兩端點(diǎn)不屬于同一集合*/{ans+=a[i].w;merge(x,y); /*合并*/}}return ans; } int main() {int i,sum;while(~scanf("%d",&n)&&n!=0){m=n*(n-1)/2;for(i=1;i<=m;i++)scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].w);sum=kruskal();printf("%d\n",sum);}return 0; }
總結(jié)
以上是生活随笔為你收集整理的hdu 1233 还是畅通工程 最小生成树(prim算法 + kruskal算法)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 浅谈 Kubernetes 服务发现
- 下一篇: 通过transmittable-thre