P3366 【模板】最小生成树

本文解析了Kruskal算法,通过边的权重排序和并查集操作,构建无环连通图的过程,同时介绍了Prim算法的随机起点选择策略,两者都是解决图论中最小生成树问题的方法。讨论了可能导致错误的两个常见原因:全局变量未设置和数组越界。
摘要由CSDN通过智能技术生成

题目:
在这里插入图片描述
输入输出样例:
input:
4 5
1 2 2
1 3 2
1 4 3
2 3 4
3 4 3
output:
7
在这里插入图片描述
注:
re的两大原因:
1.部分变量没开成全局变量
2.数组越界

代码:
Kruskal算法:
应该说是一种暴力吧,将边按照权值排序,然后按顺序选取,如果选取后会形成环则舍弃这条边,直到变成树,即边数=节点数-1

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#define maxn 211111
using namespace std;
int n,m,x,y,z;
int fa[maxn];
struct edge
{
    int x,y,z;
    bool operator<(const edge &e1)const
    {
        return z<e1.z;
    }
}e[maxn];
int getfa(int x)
{
    return (x==fa[x])?x:(fa[x]=getfa(fa[x]));
}
void merge(int x,int y)
{
    int xx=getfa(x),yy=getfa(y);
    if (xx==yy) return;
    fa[xx]=yy;
}
int main()
{
    cin>>n>>m;
    int ans=0,cnt=0;
    for (int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=m;i++) cin>>e[i].x>>e[i].y>>e[i].z;
    //cout<<"!!!"<<endl;
    //for(int i=1;i<=m;i++) cout<<e[i].x<<" "<<e[i].y<<" "<<e[i].z<<endl;
    sort(e+1,e+m+1);
    //cout<<"???"<<endl;
    //for(int i=1;i<=m;i++) cout<<e[i].x<<" "<<e[i].y<<" "<<e[i].z<<endl;
    for(int i=1;i<=m;i++)
    {
        //cout<<e[i].x<<e[i].y<<e[i].z<<endl;
        if(getfa(e[i].x)!=getfa(e[i].y))
        {
            //cout<<e[i].x<<" "<<e[i].y<<" "<<e[i].z<<" "<<"ans="<<ans;
            merge(e[i].x,e[i].y);
            ans+=e[i].z;
            cnt++;
            //cout<<" "<<"ans="<<ans<<endl;
        }
        if(cnt==n-1) break;
    }
    if(cnt!=n-1) cout<<"orz"<<endl;
    else cout<<ans<<endl;
    return 0;
}

prim算法:
随机选取一个作为起点,在与她相连的点中选取权值最小的点
需要注意,题目中给出的数据是存在自环和重边的

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <cmath>
#define maxn 5111
using namespace std;
int n,m,x,y,z;
int ans=0;
int a[maxn][maxn],lowcost[maxn],adjvex[maxn];
int main()
{
    cin>>n>>m;
    memset(a,0x3f,sizeof(a));
    for(int i=1;i<=n;i++) a[i][i]=0;
    for(int i=1;i<=m;i++)
    {
        cin>>x>>y>>z;
        a[x][y]=min(a[x][y],z);
        a[y][x]=min(a[y][x],z);
    }
    memset(lowcost,0x3f,(n+1)*sizeof(int));
    for(int i=1;i<=n;i++)
    {
        if(a[1][i])
        {
            lowcost[i]=a[1][i];
            adjvex[i]=1;
        } 
    }
    lowcost[1]=0;//表示1已经用过了
    for(int i=1;i<=n;i++)
    {
        int minn=0x3f3f3f3f;
        int temp=0;
        //for(int j=1;j<=n;j++) cout<<lowcost[j]<<" ";
        //cout<<endl;
        for(int j=1;j<=n;j++)
        {
            if(lowcost[j]!=0&&lowcost[j]<minn)
            {
                temp=j;
                minn=lowcost[j];//找到相连的最小权值的点
                //cout<<"min="<<minn<<endl;
            }
        }
        if(temp!=0) ans+=minn; 
        //cout<<"ans="<<ans<<endl;
        lowcost[temp]=0;
        //cout<<"temp"<<temp<<endl;
        //for(int j=1;j<=n;j++) cout<<a[temp][j]<<" ";
        //cout<<endl;
        for(int j=1;j<=n;j++)
        {
            if(a[temp][j]!=0&&a[temp][j]<lowcost[j])
            {
                lowcost[j]=a[temp][j];
                adjvex[j]=temp;
            }
        }
    }
    int flag=0;
    for(int i=1;i<=n;i++)
    {
        if(lowcost[i]) flag=1;
    }//判断是否联通
    if(flag) cout<<"orz"<<endl;
    else cout<<ans<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值