20250814 最小生成樹總結

引子

啊啊額,從一張圖里抽出幾條邊,組成一棵樹,無環n?1n-1n?1條邊,就是生成樹。那么邊權和最小的生成樹就叫最小生成樹,最大生成樹同理。

kruskal最小生成樹

要求kruskal最小生成樹,我們首先用結構體數組接入數據,然后按照每條邊的邊權進行升序排序,接著用并查集判斷每一條邊,看兩邊的端點是否已經在目前的生成樹里連通,否的話就加入這條邊,如果目前邊數==n?1==n-1==n?1,就breakbreakbreak,在循環外再進行判斷,看并查集數組里面是否有兩個以上的不同的祖宗,是的話就按題目要求輸出,否則輸出目前disdisdis數組的總和

例子

3
1
4
2
1
3
3
A
B
C
D
E

如上圖,我們可以知道有以下7條邊:

  1. A?\leftrightarrow?B,w=3
  2. A?\leftrightarrow?C,w=1
  3. A?\leftrightarrow?E,w=1
  4. B?\leftrightarrow?D,w=4
  5. C?\leftrightarrow?D,w=2
  6. C?\leftrightarrow?E,w=3
  7. D?\leftrightarrow?E,w=3

排完序是這樣:

  1. A?\leftrightarrow?C,w=1
  2. A?\leftrightarrow?E,w=1
  3. C?\leftrightarrow?D,w=2
  4. A?\leftrightarrow?B,w=3
  5. C?\leftrightarrow?E,w=3
  6. D?\leftrightarrow?E,w=3
  7. B?\leftrightarrow?D,w=4

接下來我們來枚舉每一條邊,具體過程如下:

  1. A?\leftrightarrow?B,此時為空圖,直接放進去。
1
A
C
  1. A?\leftrightarrow?E,加入后并不形成環,可以放進去。
1
1
A
C
E
  1. C?\leftrightarrow?D,放入。
1
1
2
A
C
E
D
  1. A?\leftrightarrow?B,也可以放入。
1
1
2
3
A
C
E
D
B

此時,我們已經添加了n?1n-1n?1條邊,已經breakbreakbreak了,所以答案算出最終為7。

實現 //////這一段每一行都有注釋

//就不給include了
struct edge{//結構體int u,v,w;//來點,去點,邊權
}a[200005];//結構體數組
bool cmp(edge x,edge y){//結構體排序return x.w<y.w;//按照邊權排序
}//一個用于排序的函數
int fa[5005],cnt,ans; //并查集數組,已選邊數,邊權總和
int find(int x){ //并查集必備return fa[x]==x?x:fa[x]=find(fa[x]);//順帶更新fa數組
}//一個用于并查集的函數
int kruskal(){//開始最小生成樹int n,m;//點的數目,邊的數目cin>>n>>m;//輸入數據for(int i=1;i<=m;i++){//輸入每條邊cin>>a[i].u>>a[i].v>>a[i].w;//接入結構體數組}//第一個循環出現了sort(a+1,a+1+m,cmp);//排序for(int i=1;i<=n;i++)fa[i]=i;//并查集數組的初始值for(int i=1;i<=m;i++){//循環每條邊int u=a[i].u,v=a[i].v,w=a[i].w;//拿出a[i]int u=find(u),v=find(v);//跑一邊并查集if(u!=v){//如果祖先不相同fa[v]=u,cnt++,ans+=w;//v認u做祖宗,邊數加一,總和加該邊的邊權}//這是一個ifif(cnt==n-1){//如果邊數達到了n-1break;//退出循環}//這也是一個if}//還有一個循環int sum=0;//查看有幾個祖宗for(int i=1;i<=n;i++){//查看并查集數組if(i==fa[i])sum++;//如果這貨認自己為祖宗,說明他就是一個祖宗}//還有最后的判斷if(sum>=2){//如果有兩個及以上個祖宗,就沒戲了return -1;//看題目要輸出什么就輸出什么}//快結束了return ans;//返回最終結果
}//不要抄襲哦!

kruskal還沒有結束,他還有時間復雜度,O(mlogmmlogmmlogm)。//////華麗結束

prim最小生成樹

額額啊,你沒猜錯,還有prim!讓我們開始吧!
首先,他非常 常見 好寫 簡單 易懂;然后實現有點像dij,畢竟都是圖論;最后,他是一種基于貪心的算法。為什么這么說呢?因為他每次都是找出一個離生成樹距離最小的一個點,然后把該點放入生成樹。

過程&例子

俗話說的好,一張圖頂一堆話……

3
1
4
2
1
3
3
1
2
3
4
5

dis數組={0,\infty, \infty,\infty,\infty$}

還是這圖,但我們這次用prim來寫。

最開始,我們把1號點放進生成樹,此時生成樹和dis長這樣:

1

dis數組={0,∞,∞,∞,∞0,\infty, \infty,\infty,\infty0} //////第0輪

然后循環n-1次,每次找出dis數組里值最小且沒被選過的一個點,把它拿出來看看他有哪些邊,如果連向的點也沒被選過,就更新他的dis。生成樹和dis依次如下:

1

dis數組={0,3,1,∞,10,3, 1,\infty,10311} //////第1輪

1
1
3

dis數組={0,3,1,2,10,3, 1,2,103121} //////第2輪

1
1
1
3
5

dis數組={0,3,1,2,10,3, 1,2,103121} //////第3輪

1
1
2
1
3
5
4

dis數組={0,3,1,2,10,3, 1,2,103121} //////第4輪

最終答案為7。

實現

const int d=1e9;//樸素
struct edge{int u,v,w;
};
vector<edge> E[5005];
int dis[5005],n,m;
bool f[5005];
int prim(){for(int i=1;i<=n;i++)dis[i]=d;dis[1]=0;//千萬別忘了for(int i=1;i<n;i++){int mn=d,u=0;for(int k=1;k<=n;k++){if(dis[k]<mn&&!f[k]){mn=dis[k],u=k;}} f[u]=1;//把自己加入生成樹中for(int j=0;j<E[u].size();j++){int v=E[u][j].v,w=E[u][j].w;if(!f[v]){dis[v]=min(dis[v],w);}}}int sum=0;for(int i=1;i<=n;i++){if(dis[i]==d){return -1;}sum+=dis[i];}return sum;
}
const int dd=1e9;//堆優化
struct node{int v,w;bool operator<(const node&a)const{return w>a.w;}
};
int n,m,dis[5005];
bool vis[5005];
vector<node> E[5005];
priority_queue<node> q;
void prim(){int cnt=0;q.push({1,0});while(!q.empty()&&cnt<=n){node d=q.top();q.pop();if(vis[d.v])continue;vis[d.v]=1,dis[d.v]=min(dis[d.v],d.w),cnt++;for(int i=0;i<E[d.v].size();i++){if(!vis[E[d.v][i].v]){q.push({E[d.v][i].v,E[d.v][i].w});}}}
}//知道為什么跟dij很像了吧!

等,時 度!

樸素 O(n2+m^2 + m2+m)
堆優化 O((n+m)logm(n+m)logm(n+m)logm)

kruskal重構樹

啊額啊,生成樹之后還有重構樹,要炸掉了 ! 可惡的kruskal !

重構樹,說白了就是把一張圖改成一棵樹,然后再這棵樹上LCA。

過程&例子

嗯?不懂?好吧,我們還是一樣來張圖:

1
5
3
1
2
4
1
2
3
4
5

首先跟前面kruskal最小生成樹的步驟一樣,把所有的邊按照邊權排序。

  1. 1 ?\leftrightarrow? 2,w=1
  2. 2 ?\leftrightarrow? 4,w=1
  3. 3 ?\leftrightarrow? 5,w=2
  4. 1 ?\leftrightarrow? 4,w=3
  5. 5 ?\leftrightarrow? 4,w=4
  6. 1 ?\leftrightarrow? 3,w=5

接著,我們循環把所有的邊拿出來,查看他們的祖宗是否相同,否的話就把這條邊掛在用于LCA的樹上。

咋掛?這時,我們需要擴域,也就是建造虛擬節點,cnt。

循環前把cnt設為n,每次要掛邊時,cnt++,然后讓cnt當u和v的父親,同時更新并查集數組和記錄每個cnt的左兒子節點和右兒子節點的距離的b數組。

以下為得出的樹和各個數組:

6
1
2
7
4
8
3
5
9
123456789
fa668787999
b/////1124

現在樹建好了,就等著LCA啦!

實現

#include<bits/stdc++.h>
using namespace std;
struct edge{int u,v,w;
}a[600005];
bool cmp(edge x,edge y){return x.w<y.w;
}
int fa[200005],b[200005],dep[200005],fc[25][200005],cnt;//并查集數組,每個cnt的左兒子節點和右兒子節點的距離,深度,次方父親數組
vector<int> E[200005];
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);
}
void dfs(int x,int f){dep[x]=dep[f]+1;for(int i=1;i<=20;i++)fc[i][x]=fc[i-1][fc[i-1][x]];for(int i=0;i<E[x].size();i++){int v=E[x][i];if(v==f)continue;fc[0][v]=x;dfs(v,x);}
}
int LCA(int x,int y){if(dep[x]>dep[y]){swap(x,y);}for(int i=19;i>=0;i--){if(dep[fc[i][y]]>=dep[x]){y=fc[i][y];}}if(x==y)return x;for(int i=19;i>=0;i--){if(fc[i][x]!=fc[i][y]){x=fc[i][x];y=fc[i][y];}}return fc[0][x];
}
int main(){int n,m;cin>>n>>m;cnt=n;for(int i=1;i<=m;i++){cin>>a[i].u>>a[i].v>>a[i].w;}sort(a+1,a+1+m,cmp);for(int i=1;i<=n*2;i++)fa[i]=i;//由于進行了擴域,所以*2for(int i=1;i<=m;i++){//把所有邊拿出來瞅瞅int u=a[i].u,v=a[i].v,w=a[i].w;u=find(u),v=find(v);if(u!=v){fa[v]=fa[u]=++cnt;b[cnt]=w;E[u].push_back(cnt);E[v].push_back(cnt);E[cnt].push_back(u);E[cnt].push_back(v);}}for(int i=1;i<=cnt;i++){//一系列LCAif(i==fa[i]){dfs(i,0);}}int q;cin>>q;while(q--){//q次詢問int x,y;cin>>x>>y;if(find(x)!=find(y)){cout<<"impossible"<<endl;}else{cout<<b[LCA(x,y)]<<endl;}}return 0;
}

俗話說得好,所有的代碼都可以縮成兩行。

#include<bits/stdc++.h> //獻上最后的代碼
using namespace std;struct edge{int u,v,w;}a[600005];vector<int> E[200005];int fa[200005],b[200005],dep[200005],fc[25][200005],cnt,n,m,q;bool cmp(edge x,edge y){return x.w<y.w;}int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}void dfs(int x,int f){for(int i=1;i<=20;i++){fc[i][x]=fc[i-1][fc[i-1][x]],dep[x]=dep[f]+1;}for(int i=0;i<E[x].size();i++){int v=E[x][i];if(v==f){continue;}else{fc[0][v]=x,dfs(v,x);}}}int LCA(int x,int y){if(dep[x]>dep[y])swap(x,y);for(int i=20;i>=0;i--){if(dep[fc[i][y]]>=dep[x])y=fc[i][y];}if(x==y)return x;for(int i=20;i>=0;i--){if(fc[i][x]!=fc[i][y])x=fc[i][x],y=fc[i][y];}return fc[0][x];}int main(){cin>>n>>m;for(int i=1;i<=m;i++){cin>>a[i].u>>a[i].v>>a[i].w;}sort(a+1,a+1+m,cmp);for(int i=1;i<=n*2;i++)fa[i]=i,cnt=n;for(int i=1;i<=m;i++){int u=a[i].u,v=a[i].v,w=a[i].w,fu=find(u),fv=find(v);if(fu!=fv)fa[fu]=fa[fv]=++cnt,b[cnt]=w,E[cnt].push_back(fu),E[cnt].push_back(fv),E[fu].push_back(cnt),E[fv].push_back(cnt);}for(int i=1;i<=cnt;i++)if(fa[i]==i)dfs(i,0);cin>>q;while(q--){int x,y;cin>>x>>y;find(x)!=find(y)?cout<<"impossible\n":cout<<b[LCA(x,y)]<<endl;}return 0;}

完結萬歲!!!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/93080.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/93080.shtml
英文地址,請注明出處:http://en.pswp.cn/web/93080.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

數據大集網:實體店獲客引流的數字化引擎,解鎖精準拓客新密碼?

?在實體店面臨流量焦慮、獲客成本攀升的當下&#xff0c;實體店獲客引流工具的重要性愈發凸顯。如何在激烈的市場競爭中精準觸達目標客戶、構建可持續的客流增長模式&#xff1f;數據大集網憑借其創新的智能獲客體系與全鏈路服務能力&#xff0c;正成為萬千實體店突破增長瓶頸…

nginx --ssl證書生成mkcert

github https://github.com/FiloSottile/mkcert/releases網盤下載地址 https://pan.baidu.com/s/1XI0879pqu7HXZMnmQ9ztaw 提取碼: 1111windows使用示例

守拙以致遠:個人IP的長青之道|創客匠人

2025年被認為是AI應用全面爆發的一年。各種人工智能工具在寫作、制圖、剪輯等領域廣泛使用&#xff0c;大大提升了個人和團隊的工作效率。對于個人IP而言&#xff0c;這類工具的出現確實帶來了新的機會&#xff0c;但也伴隨著一種現象——一些人開始過度依賴甚至神化AI&#xf…

USB 3.0 LTSSM 狀態機

USB2.0在電源供應后&#xff0c;通過Pull Up D-來決定枚舉LS&#xff0c;Pull Up D有一個USB高速握手過程&#xff0c;來決定HS FS。USB3.0則會通過鏈路訓練&#xff08;Link Training&#xff09;&#xff0c;來準備USB3.0通信。每當我們插上USB線的時候&#xff0c;對于3.0的…

MySQL窗口函數與PyMySQL以及SQL注入

MySQL窗口函數與PyMySQL實戰指南&#xff1a;從基礎到安全編程 引言 在數據處理和分析領域&#xff0c;MySQL作為最流行的關系型數據庫之一&#xff0c;其窗口函數功能為數據分析提供了強大的支持。同時&#xff0c;Python作為數據分析的主要語言&#xff0c;通過PyMySQL庫與My…

高級項目——基于FPGA的串行FIR濾波器

給大家安利一個 AI 學習神站&#xff01;在這個 AI 卷成紅海的時代&#xff0c;甭管你是硬核開發者還是代碼小白&#xff0c;啃透 AI 技能樹都是剛需。這站牛逼之處在于&#xff1a;全程用 "變量名式" 幽默 生活化類比拆解 AI&#xff0c;從入門到入土&#xff08;啊…

JPrint免費的Web靜默打印控件:PDF打印中文亂碼異常解決方案

文章目錄JPrint是什么&#xff1f;中文亂碼&#xff08;Using fallback font xxx for xxxx&#xff09;1.字體嵌入2.客戶機字體安裝開源地址相關目錄導航使用文檔端口號修改代理使用場景打印服務切換中文亂碼解決方案 JPrint是什么&#xff1f; JPrint是一個免費開源的可視化靜…

MFT 在零售行業的實踐案例與場景:加速文件集成與業務協作的高效方案

零售行業競爭激烈、數字化轉型迭代迅速&#xff0c;業務對數據與檔案的傳輸、處理和整合要求極高。無論是新品上市市場數據&#xff0c;還是供應鏈物流單據&#xff0c;集成方式不論是通過API或是檔案傳輸, 對于傳輸的穩定性,安全性與性能, 都會直接影響決策效率與顧客體驗。MF…

OSG+Qt —— 筆記1 - Qt窗口加載模型(附源碼)

?? OSG/OsgEarth 相關技術、疑難雜癥文章合集(掌握后可自封大俠 ?_?)(記得收藏,持續更新中…) OSG+Qt所用版本皆為: Vs2017+Qt5.12.4+Osg3.6.5+OsgQt(master) 效果 代碼(需將cow.osg、reflect.rgb拷貝至工程目錄下) OsgForQt.ui main.cpp

開源安全云盤存儲:Hoodik 實現端到端數據加密,Docker快速搭建

以下是對 Hoodik 的簡單介紹&#xff1a; Hoodik 是一個使用 Rust 和 Vue 開發的輕量級自托管安全云存儲解決方案采用了非對稱RSA密鑰對和AES混合加密策略&#xff0c;從文件存儲加密到數據鏈路加密&#xff0c;全程保證數據安全支持Docker一鍵私有部署&#xff0c;數據和服務…

[C++] Git 使用教程(從入門到常用操作)

1. Git 簡介 Git 是一款分布式版本控制系統&#xff0c;用來跟蹤文件變化、協作開發、管理項目版本。 它是開源的&#xff0c;由 Linus Torvalds 在 2005 年開發&#xff0c;廣泛用于開源與企業項目中。 2. 安裝 Git Windows 前往 Git 官網 下載并安裝。 安裝時建議勾選 Git…

實盤回測一體的期貨策略開發:tqsdk獲取歷史數據并回測,附python代碼

原創內容第969篇&#xff0c;專注AGI&#xff0c;AI量化投資、個人成長與財富自由。 星球好多同學希望說說實盤&#xff0c;我們就從實盤開始吧。 我們選擇tqsdk給大家講解&#xff0c;tqsdk支持免費注冊&#xff0c;使用模擬賬戶&#xff0c;歷史和實時數據&#xff0c;方便…

大模型推理框架vLLM 中的Prompt緩存實現原理

背景&#xff1a;為什么需要Prompt緩存模塊&#xff1f;在大模型問答多輪對話應用場景中&#xff0c;不同請求的 Prompt 往往有相同的前綴&#xff0c;比如&#xff1a;第一次問答&#xff1a;你是一名專業的電子產品客服&#xff0c;負責回答客戶關于手機產品的咨詢。請根據以…

Python之Django使用技巧(附視頻教程)

概述 Django 是一個高級的 Python Web 框架&#xff0c;遵循 “batteries-included”&#xff08;內置電池&#xff09;理念&#xff0c;提供了構建 Web 應用所需的大部分組件&#xff0c;讓開發者可以專注于業務邏輯而不是底層細節。視頻教程&#xff1a;https://pan.quark.cn…

sqli-labs通關筆記-第44關 POST字符型堆疊注入(單引號閉合 手工注入+腳本注入3種方法)

目錄 一、堆疊注入 二、源碼分析 1、代碼審計 2、SQL注入安全性分析 三、堆疊手注法 1、進入靶場 2、正確用戶名密碼登錄 3、堆疊注入 4、查看數據庫 四、聯合手注法 1、獲取列數 2、確認回顯位 3、獲取數據庫名 4、獲取表名 5、獲取列名 6、獲取字段 7、總結…

從深度偽造到深度信任:AI安全的三場攻防戰

前言當大模型開始“睜眼”看世界&#xff0c;偽造者也開始“閉眼”造世界。2025 WAIC釋放出的信號很明確&#xff1a;沒有AI安全底座&#xff0c;就沒有產業智能化的高樓。WAIC 把“安全”擺在與“創新”同等重要的位置&#xff0c;形成了“1 份共識框架&#xff0b;2 份重磅報…

【C++】哈希的應用:位圖和布隆過濾器

目錄 一、位圖 1.1 位圖的概念 1.2 位圖的實現 1.3 位圖的應用 二、布隆過濾器 2.1 布隆過濾器的提出 2.2 布隆過濾器的概念 2.3 布隆過濾器的插入和查找 2.4 布隆過濾器的刪除 2.5 布隆過濾器的優點 2.6 布隆過濾器的缺點 一、位圖 1.1 位圖的概念 1. 面試題 給4…

C語言:指針(4)

1. 回調函數回調函數就是指通過函數指針調用的函數。如果將函數指針作為參數傳遞給另一個函數&#xff0c;另一個函數根據指針來調這個函數&#xff0c;那么被調用的函數就是回調函數。回調函數不是由這個函數的實現方直接調用&#xff0c;而是在特定的條件下由另一方調用的。例…

vue--video使用動態src時,視頻不更新

問題描述 在 Vue項目中&#xff0c;嘗試動態更新 標簽的 元素 src 屬性來切換視頻時&#xff0c;遇到了一個問題&#xff1a;即使 src 已更改&#xff0c;瀏覽器仍不顯示視頻。 <template><video width"100%" height"100%" controlspause"…

計算機視覺--opencv(代碼詳細教程)(一)

在計算機視覺的廣袤領域中&#xff0c;OpenCV 是一座極為關鍵的里程碑。無論是在前沿的學術研究&#xff0c;還是在蓬勃發展的工業界&#xff0c;OpenCV 憑借其強大的功能與高效的性能&#xff0c;為開發者提供了豐富的圖像處理和計算機視覺算法&#xff0c;助力無數項目落地。…