Kruskal/Prim最小生成樹
阿新 • • 發佈:2019-02-16
最小生成樹
定義
- 樹
連通,但不成環
- 最小
邊的代價和最小
規律
舉例
以四個點為例,共需三條線即可。
- 同類共四種(任意去掉其中一條線)
- 同類共四種
- 同類共四種(任意去掉其中一條線)
- 同類共四種(任意去掉其中一條線)
- 同類共四種(任意去掉其中一條線)
總結
n個點生成樹需要(n-1)條線來連通。
克魯斯卡爾 Kruskal
貪心思想+並查集(檢查是否成環)
- 用邊權按從小到大排序
- 初始化並查集
- 迴圈加邊,檢查是否成環 (n-1條)
普萊姆 Prim
運用了,貪心思想
貪心:
一種結果很準確,但是佔的空間很大很大。
所以我們這次要用這種很好的方法來做。
Prim
Prim演算法跟Dij和Bellman-演算法一樣。
也是使用了”藍白點”的思想
Prim演算法每次迴圈都要將一個藍點u變為一個白點,這樣迴圈,min[u]裡面的權還是最小的
初始化
對於flag操作的問題:
memset(flag,true,sizeof flag);
解釋:
把flag全部填為真(True)
對於f
for (int i=1;i<=n;++i) f[i]=0x7fffffff; f[1]=0;
注意哦,這裡不能用memset,那樣會卡爆!!
解釋:
如果呢?到了
對於map
memset(map,0,sizeof map);
2.迴圈
for (int i=1;i<=n-1;++i){ //1.在f中查詢最小權值的編號(且flag==false) int min1=0x7fffffff; int v=-1; for (int j=1;j<=n;++j){ if (flag[j] && min1>f[j]){ min1=f[j]; v=j; } } //2.標記v flag[v]=false; //3.更新v發出的邊 for (int j=1;j<=n;++j){ if (flag[j] && map[v][j]!=0){ if (map[v][j]<f[j]) f[j]=map[v][j]; } } }
附上完整程式
//鄰接矩陣
#include <cstdio>
#include <cstring>
const int N=110;
int map[N][N],n,m,x,y,z,f[N];
bool flag[N];
int main(){
freopen("prim.in","r",stdin);
freopen("prim.out","w",stdout);
scanf("%d%d",&n,&m);
memset(map,0,sizeof map);
for (int i=1;i<=m;++i){
scanf("%d%d%d",&x,&y,&z);
map[x][y]=z; map[y][x]=z;
}
memset(flag,true,sizeof flag);
for (int i=1;i<=n;++i) f[i]=0x7fffffff; f[1]=0;
for (int i=1;i<=n-1;++i){
//1.在f中查詢最小權值的編號(且flag==false)
int min1=0x7fffffff;
int v=-1;
for (int j=1;j<=n;++j){
if (flag[j] && min1>f[j]){
min1=f[j];
v=j;
}
//更新我的指令
//
}
//2.標記v
flag[v]=false;
//3.更新v發出的邊
for (int j=1;j<=n;++j){
if (flag[j] && map[v][j]!=0){
if (map[v][j]<f[j]) f[j]=map[v][j];
}
}
}
int ans=0;
for (int i=1;i<=n;++i) ans+=f[i];
printf("%d",ans);
return 0;
}