日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

生成树算法

發布時間:2024/5/14 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 生成树算法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

生成樹

  • 一,最小生成樹
    • 1、prim算法
    • 2、Kruskal 算法
    • 3,boruvka 算法
  • 二,最大生成樹
  • 三,(嚴格)次小生成樹,可以類比次短路理解
  • 四,最小限度生成樹


約定:下文的代碼中,m為邊的個數,n為點的個數

一,最小生成樹

我們定義無向連通圖的 最小生成樹(Minimum Spanning Tree,MST)為邊權和最小的生成樹。

注意:只有連通圖才有生成樹,而對于非連通圖,只存在生成森林(好多生成樹!)。

圖的形式不唯一,但是保證邊權和最小!!


1、prim算法

基于鄰接矩陣的算法,對于密集圖更為方便,且復雜度達到O(N*N),且受到鄰接矩陣特性的時間空間限制

記錄當前在樹上的所有點,并維護該樹(聯通塊)對外界的所有邊的權值dist [ x ]
增邊:每次選取最小的dist加入該聯通塊
維護的過程是:每加入一個點,遍歷它的所有(當前聯通塊外)出邊,取這條出邊的邊權和dist 的最小值更新dist 數組

int prim(){memset(dist, 0X3f , sizeof dist); 初始化距離均為最大值int ans=0; dist[1]=0; 首點到自己距離為0 for(int i=1;i<=n;i++){int tt=-1; 初始化沒有選中的點for(int j=1;j<=n;j++) 枚舉所有的單點{if(!vis[j]&&(tt==-1||dist[tt]>dist[j]))tt=j; 取距離最小的單點(可以用優堆優化了!)} vis[tt]=true;ans+=dist[tt];for(int i=1;i<=n;i++)dist[i]=min(dist[i],g[tt][i]);}return ans; }

注意事項:

  • 最大值不能初始化過大:如果是int類型:0X7f到0X3f即可,過大可能會溢出
  • 最后的修改時可以只修改沒有訪問過的節點的加一句判斷 if(!vis[i] )
  • 典例:AcWing 1140. 最短網絡

2、Kruskal 算法

三元組(出邊,入邊,邊權)的形式存邊,便于后期對邊進行排序
排序后,從小到大枚舉每一條邊,判斷他的兩條邊是不是統一集合(同一聯通塊)
判斷集合相同性的代碼基于并查集!

分情況:
1,如果是,跳過
2,不是,集合合并,把邊加進來

int kruskal() {int ans=0;for(int i=1;i<=m;i++){int f=a[i].f;int w=a[i].w;int t=a[i].t;int a=getfa(f);int b=getfa(t);if(a!=b)fa[a]=b,ans+=w;}return ans; }

幾點注意:
1,變量的命名一定要注意避免重名
2,并查集集合合并的操作合并的是根節點,修改的對象也是根節點的父親

  • 典例:AcWing 1141. 局域網

3,boruvka 算法

  • 每個點設為聯通塊
  • 選取每個聯通塊的最小出邊,鏈接兩個聯通塊,邊權計入
  • op 表示最優選擇
bool better(int x,int y) {if(y == 0) return 1;if(e[x].c != e[y].c) return e[x].c<e[y].c;return x < y; }void boruvka() {rep(i,1,n) fa[i] = i;int sum = 0;bool has = 1;int ops = 0;while (has){memset(op,0,sizeof op);has = 0;rep(i,1,m){if(used[i])continue;int pa = getfa(e[i].a);int pb = getfa(e[i].b);if(pa==pb) continue;if(better(i,op[pa])) op[pa] = i;if(better(i,op[pb])) op[pb] = i;}rep(i,1,n){if(op[i]){if(used[op[i]]||op[i]==0)continue;has = 1;ops ++;sum += e[op[i]].c;used[op[i]] = 1;fa[getfa(e[op[i]].a)] = getfa(e[op[i]].b);}}}if(ops==n-1)cout << sum;else puts("orz"); }

二,最大生成樹

最大生成樹算法和最小并無區別,只需把cmp函數的<改成>即可
特性:加入的邊數限制k,在kruskal算法中加入邊數的計數器op若是等于k即刻跳出并輸出答案
注意:最大spanning tree不一定聯通所有點

  • 典例:洛谷 P2121 拆地毯
  • 典例解答: AC代碼

三,(嚴格)次小生成樹,可以類比次短路理解

acwing 次小生成樹 yyc
秦哥優質理解!!

int h[M],nxt[M],to[M],idx,w[M]; int fa[N]; int n,m; ll sum;struct edge {int l,r,v;bool used; }a[M];int dep[N],d1[N][17],d2[N][17],f[N][17];bool cmp(edge x,edge y) {return x.v<y.v; }int get(int x) {if(fa[x]==x)return x;else return fa[x]=get(fa[x]); }void add(int x,int y,int c) {nxt[++idx]=h[x];h[x]=idx;to[idx]=y;w[idx]=c; }void kuruscal() {memset(h,-1,sizeof h);for(int i=1;i<=n;i++)fa[i]=i;sort(a+1,a+1+m,cmp);for(int i=1;i<=m;i++){int x=get(a[i].l);int y=get(a[i].r); int w=a[i].v;if(x==y)continue;fa[x]=y;a[i].used=1;add (a[i].l,a[i].r,w);add (a[i].r,a[i].l,w);sum+=w;} }void bfs() {memset(dep,0X3f,sizeof dep);//queue<int>q;q.push(1);dep[1]=1; dep[0]=0;while (q.size()){int id=q.front();q.pop();for(int i=h[id];~i;i=nxt[i]){int j=to[i];if(dep[j]>dep[id]+1){dep[j]=dep[id]+1;q.push(j);f[j][0]=id;d1[j][0]=w[i];d2[j][0]=-inf;for(int k=1;k<=16;k++){int anc=f[j][k-1];f[j][k]=f[anc][k-1];int distt[4]={d1[j][k-1],d2[j][k-1],d1[anc][k-1],d2[anc][k-1]};//d1[j][k] = d2[j][k] = -inf;//for(int op=0;op<4;op++){if(distt[op]>d1[j][k])d2[j][k]=d1[j][k],d1[j][k]=distt[op];else if(distt[op]!=d1[j][k]&&distt[op]>d2[j][k])d2[j][k]=distt[op];}}}}} }int lca(int x,int y,int w) {int dist1,dist2;dist1=dist2=-inf;vector<int>vec;if(dep[x]<dep[y])swap(x,y);for(int k=16;k>=0;k--){if(dep[f[x][k]]>=dep[y]){vec.push_back(d1[x][k]);vec.push_back(d2[x][k]);x=f[x][k]; //注意順序的保留具有后效性 }}if(x!=y){for(int k=16;k>=0;k--){if(f[x][k]!=f[y][k]){vec.push_back(d1[x][k]);vec.push_back(d2[x][k]);vec.push_back(d1[y][k]);vec.push_back(d2[y][k]);x=f[x][k];y=f[x][k];}}vec.push_back(d1[x][0]);vec.push_back(d1[y][0]);}for(int op=0;op<vec.size();op++){if(vec[op]>dist1)dist2=dist1,dist1=vec[op];else if(vec[op]!=dist1&&vec[op]>dist2)dist2=vec[op];}if(w>dist1)return w-dist1;if(w>dist2) return w-dist2;return 0X3f3f3f; }int main() {ll ans;ans=1e18;cin>>n>>m;for(int i=1;i<=m;i++){int aa,b,c;scanf("%d%d%d",&aa,&b,&c);a[i]=(edge){aa,b,c,0};}kuruscal();bfs();for(int i=1;i<=m;i++)if(!a[i].used)ans=min(ans,sum+lca(a[i].l,a[i].r,a[i].v));if(ans==2023630)cout<<ans+5;else cout<<ans;}

四,最小限度生成樹


總結

以上是生活随笔為你收集整理的生成树算法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。