图论讲解(3)——最小生成树(基础)
關于這個東西,有的童鞋又要開始蒙了,最小生成樹是個什么鬼?!
前面我們已經說過樹是什么東西了,所謂最小生成樹嘛,最小嘛,那就是所有生成的樹中最小的那個唄!
太別扭了對吧?
好,我們來看看官方回答。
一個有 n 個結點的連通圖的生成樹是原圖的極小連通子圖,且包含原圖中的所有 n 個結點,并且有保持圖連通的最少的邊。最小生成樹可以用kruskal(克魯斯卡爾)算法或prim(普里姆)算法求出。
prim算法
Prim算法是通過先選取一個點,然后不斷加入點的一個過程。 ’初始化:V’={x},E’={},x是隨便一個節點; ’重復下列操作,直到V’=V: ? ′在E集合當中選擇最小的邊<u,v>使得u∈V’但是v?V’; ? ′V’加入節點v,E’加入<u,v>; ? ′(V’,E’)則為所求的最小生成樹。′我們可以對該算法里面的各個步驟分別考慮: ′初始化:V’={x},E’={},x是隨便一個節點; ? ? ? ? ? ? ?這一步只需要隨便選取一個點即可; ′重復下列操作,直到V’=V: ′在E集合當中選擇最小的邊<u,v>使得u∈V’但是v?V’; ′V’加入節點v,E’加入<u,v>; 對于上面的第二步,實際上我們只需要對于每一個點維護一個V’集合中的點到達該點的最短距離。 然后每次掃描一遍數組找到我們所需要的v加入V’; 復雜度為O(N^2+M). 對于上面的第二步操作,我們實際上可以通過堆(優先隊列)維護一個滿足u∈V’但是v?V’的邊集,那么我們就能迅速取出滿足要求的邊; 然后當改變了V’的時候,我們就可以根據新加入的節點v對原有的堆進行刪除和插入操作。 需要注意的是,當我們用優先隊列實現的時候,我們需要將刪除操作延遲。 復雜度為O((N+M)logN). 嗶嗶了這么多,我們直接來看看代碼吧!(當然先是一些板子)
#include<bits/stdc++.h>
using namespace std;
const int maxn=10010;
int n,m,edge[maxn][maxn];
int dis[maxn];
bool boo[maxn];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
edge[x][y]=edge[y][x]=z;
}
dis[1]=0;
boo[1]=true;
for(int i=2;i<=n;++i)
dis[i]=123456789;
for(int i=2;i<=n;++i)
if(edge[1][i])
dis[i]=min(dis[i],edge[1][i]);
int ans=0;
for (int i=1;i<=n-1;++i)
{
int maxd=0;
for(int j=1;j<=n;++j)
{
if(!boo[j])
if(maxd==0||dis[maxd]>dis[j])
maxd=j;
}
boo[maxd]=true;
ans+=dis[maxd];
for(int j=1;j<=n;++j)
{
if(!boo[j])
if(edge[maxd][j])
dis[j]=min(dis[j],edge[maxd][j]);
}
}
printf("%d\n",ans);
return 0;
}
是不是感覺k算法比p算法好理解啊(暫且先那么叫)
據徐大佬說k算法好像比p算法快,唉,如果讓你寫的話,是不是一定選擇這種又快有好寫還有好理解的代碼啊。
反正讓我寫的的話,我一定會選擇寫k算法的,但是這僅限于裸地最小生成樹的題。
對于有的題k算法是不如p算法的,甚至有的時候,你用k算法還不一定能做出來。
好了,廢話不多說了,我們來看看代碼吧。
?
#include <bits/stdc++.h> using namespace std;const int maxn=100000+15; struct Edge {int x,y,z; }edge[maxn]; bool cmp(Edge a,Edge b) {return a.z<b.z; } int top[maxn],x,y,z,n,m; int found(int x) {if (top[x]==x) return x;top[x]=found(top[x]);return top[x]; } int main() {scanf("%d%d",&n,&m);for (int i=1;i<=m;i++){int x,y,z;scanf("%d%d%d",&x,&y,&z);edge[i].x=x;edge[i].y=y;edge[i].z=z;}sort(edge+1,edge+m+1,cmp);for (int i=1;i<=n;i++) top[i]=i;int ans=0;for (int i=1;i<=m;i++){int x=edge[i].x,y=edge[i].y;int fx=found(x),fy=found(y);if (fx==fy) continue;top[fx]=fy;ans+=edge[i].z;}printf("%d\n",ans);return 0; }?
?
?
最小瓶頸生成樹
有的童鞋看到這個就要抓狂了,這又是個什么鬼!!!
哎呀,不要著急,我們來具體看一看這個東西。
最小瓶頸生成樹:在一個圖中找出一棵樹,使這棵樹的最大權值最小。
給你一個這樣的關系吧:最小生成樹一定是最小瓶頸生成樹,最小瓶頸生成樹不一定是最小生成樹。
最優比率生成樹
給出一個圖,每條邊的權值是(a,b)的形式。求出一個生成樹使得suma/sumb最小。 權值均為正值 ′二分答案 ′Suma/sumb>K ′Suma>Sumb*K, ′Sum(a-kb)>0 ′一般的最大生成樹 #include <bits/stdc++.h> using namespace std;const int maxn=100000+15; struct Edge {int x,y,a,b;Edge(int x=0,int y=0,int a=0,int b=0):x(x),y(y),a(a),b(b) {} }edge[maxn],edge2[maxn]; int mst() {return 0; } int change(int K) {for (int i=1;i<=m;i++)edge2[i]=Edge(edge[i].x,edge[i].y,edge[i].a-K*edge[i].b,0);return 0; } int main() {scanf("%d%d",&n,&m);for (int i=1;i<=m;i++){int x,y,a,b;scanf("%d%d%d%d",&x,&y,&a,&b);edge[i]=Edge(x,y,a,b);}int l=0,r=1000000,mid;while (l+1<r){mid=(l+r)/2;change(mid);if (mst()<=0) r=mid;else l=mid;}printf("%d\n",r);return 0; }?
轉載于:https://www.cnblogs.com/z360/p/6853637.html
總結
以上是生活随笔為你收集整理的图论讲解(3)——最小生成树(基础)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【NOIP2012-开车旅行】
- 下一篇: Fastjson反序列化漏洞研究