最小生成树Prim算法 Kruskal算法

时间:2023-02-07 11:41:12

Prim算法(贪心策略)N^2

选定图中任意定点v0,从v0开始生成最小生成树

树中节点Va,树外节点Vb

最开始选一个点为Va,其余Vb,

之后不断加Vb到Va最短距离的点

1.初始化d[v0]=0,其他d[i]=正无穷。d表示Vb电到i的最小距离

2.经过n次如下步骤,得到一颗喊n节点n-1边的最小生成树

(1)选择一个未标记的k,并且d[k]的值最小

(2)标记点k进入树Va

(3)以k为中间点,修改未标记的点j,即Vb中的点到Va的距离值;

3.得到最小生成树t

#include<iostream>
#include<iomanip> 
#include<cstring>
using namespace std;
const int INF=0x7fffffff/2;
int vst[505];//标记i是否加入最小生成树Va中 
int d[505];//i与当前生成树中的点有连边的边长最小值 
int g[505][505],n,m,ans=0;//g存边和权值 
void read(){//读入数据 
    int i,j,x,y,w;
    cin>>n>>m;//n点m边 
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        g[i][j]=INF;//清零 
    for(i=1;i<=m;i++){
        cin>>x>>y>>w;
        g[x][y]=g[y][x]=w;//记录边和权值 
    }
}
void prim(int v0){
    int i,j,k,minn;
    memset(vst,0,sizeof(vst));
    for(i=1;i<=n;i++) d[i]=INF;//初始化 
    d[v0]=0;//最初节点 
    ans=0;
    for(i=1;i<=n;i++){//选择n个点 
        minn=INF;
        for(j=1;j<=n;j++)//选择最小边 ,Vb到Va 
        if(vst[j]==0&&minn>d[j]){
            minn=d[j];k=j;
        }
        vst[k]=1;//标记 ,加入到Va 
        ans+=d[k];//加上边的权值 
        for(j=1;j<=n;j++)//修改d数组 
        if(vst[j]==0&&d[j]>g[k][j])
        d[j]=g[k][j];
    }
}
int main(){
    read();
    prim(1);
    cout<<ans<<endl;
    return 0;
} 

Kruskal算法(贪心策略)nlogn

算法步骤:

1.将G中的带权边由小到大排序

2.按照权值由小到大依次选边。诺形成环就放弃这一条,否则标记当前边并计数;

3.重复2.直到生成树有n-1条边。

否则遍历完边取不到n-1,就不存在最小生成树。

***如何判断环:用并查集:判断新加入的边的两个端点如果在并查集同一集合则成环;

否则保存当前边,并合并涉及的两个集合。

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=100005;
struct edge{
    int x,y,z;
}a[maxn];
int n,m,prt[maxn],ans=0,bj;
bool cmp(const edge &x,const edge &y){
    return x.z<y.z;
} 
int getfather(int x){//找祖先 
    if(prt[x]==x) return x;
    prt[x]=getfather(prt[x]);
    return prt[x];
}
void kruskal(){//核心程序 
    int f1,f2,k,i;
    k=0;//记录已经加入的边数 
    for(i=1;i<=n;i++) prt[i]=i;//初始化 
    for(i=1;i<=m;i++){
        f1=getfather(a[i].x);//并查集??不太懂 
        f2=getfather(a[i].y);
        if(f1!=f2){
            ans+=a[i].z;
            prt[f1]=f2;//合并不相同的两个集合 
            k++;
            if(k==n-1) break;
        }
    }
    if(k<n-1){
        cout<<"Impossible"<<endl;bj=0;
        return ;
    }
}
int main(){ 
cin>>n>>m; 
ans=0;bj=1;
for(int i=1;i<=m;i++)
cin>>a[i].x>>a[i].y>>a[i].z;
sort(a+1,a+m+1,cmp);
kruskal();
if(bj) cout<<ans<<endl;
return 0;
}