(最小生成树问题:Prim,Kruskal)村村通公路

本文介绍了一种利用最小生成树算法解决农村公路建设问题的方法,通过普里姆算法和克鲁斯卡尔算法来寻找连接所有村庄的最低成本方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

3362数据结构实验之图论六:村村通公路


Problem Description

当前农村公路建设正如火如荼的展开,某乡镇政府决定实现村村通公路,工程师现有各个村落之间的原始道路统计数据表,表中列出了各村之间可以建设公路的若干条道路的成本,你的任务是根据给出的数据表,求使得每个村都有公路连通所需要的最低成本。

Input

连续多组数据输入,每组数据包括村落数目N(N <= 1000)和可供选择的道路数目M(M <=
3000),随后M行对应M条道路,每行给出3个正整数,分别是该条道路直接连通的两个村庄的编号和修建该道路的预算成本,村庄从1~N编号。

Output

输出使每个村庄都有公路连通所需要的最低成本,如果输入数据不能使所有村庄畅通,则输出-1,表示有些村庄之间没有路连通。

Example Input

5 8
1 2 12
1 3 9
1 4 11
1 5 3
2 3 6
2 4 9
3 4 4
4 5 6

Example Output

19

解题思路
构造最小生成树的算法有普里姆算法(Prim)和克鲁斯卡尔算法(Kruskal)
1.普里姆算法(Prim):归并顶点(加顶点法),与边数无关,适于稠密图;
基本思路:首先取任意一个顶点加入最小生成树,然后再取一个距离已生成的最小生成树的顶点加入最小生成树,直至所有的点都加入最小生成树。d[i]为顶点i与最小生成树的距离,vis[i]为1代表顶点i已加入最小生成树。
2.克鲁斯卡尔算法(Kruskal):归并边(加边法),适于稀疏图。实现过程借助并查集。
基本思路:首先把边从小到大排序,按顺序取出每条边,如果它连接的每两个顶点目前并不连通,则将该边加入最小生成树,否则舍弃该边。
并查集知识补充如下。
并查集详解


代码1:普里姆算法(Prim)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=1010;
int n,m,vis[maxn],d[maxn],mp[maxn][maxn];
long long ans;

bool Prim(){
    memset(vis,0,sizeof(vis));
    ans=0;
    vis[1]=1;  //加入顶点1
    d[1]=0;
    for(int i=2;i<=n;i++) //更新各点与最小生成树的距离
        d[i]=mp[1][i];  

    int tmp;
    for(int i=1;i<n;i++){  //还有n-1个顶点待加入
        int mn=INF;
        for(int j=2;j<=n;j++){
            if(!vis[j]&&d[j]<mn){
                mn=d[j];
                tmp=j;
            }
        }

        if(mn==INF) return false;
        ans+=mn;
        vis[tmp]=1;
        for(int j=2;j<=n;j++){ //更新各点与最小生成树的距离
            if(!vis[j]&&mp[tmp][j]<d[j]) d[j]=mp[tmp][j];
        }
    }
    return true;
}


int main(){
    while(~scanf("%d%d",&n,&m)){
        int u,v,w;
        memset(mp,0x3f,sizeof(mp));
        for(int i=0;i<m;i++){
            scanf("%d%d%d",&u,&v,&w);
            mp[u][v]=mp[v][u]=w;
        }
        if(!Prim()) ans=-1;
        printf("%lld\n",ans);
    }
    return 0;
}

代码2:克鲁斯卡尔算法(Kruskal)

#include<cstdio>
#include<stdlib.h>
#include<algorithm>
using namespace std;
const int maxn=1010;
const int maxm=3010;
int n,m;
int par[maxn];

struct edge{
    int from,to;
    int w;
    bool operator <(const edge &e)const{
        return w<e.w;
    }
}E[maxm];

int fi(int x){
    if(par[x]!=x) return par[x]=fi(par[x]);
    return x;
}

//非递归形式
/*
int fi(int t)
{
    int r=t;
    while(par[r]!=r)
        r=par[r];
    int i=t,j;
    while(i!=r)//压缩路径
    {
        j=par[i];
        par[i]=r;
        i=j;
    }
    return r;
}
*/

bool Same(int x,int y){
    return fi(x)==fi(y);
}

void join(int x,int y){
    int fx=fi(x),fy=fi(y);
    if(fx!=fy) par[fy]=fx;
}

long long kruskal(){
    for(int i=1;i<=n;i++)//各顶点自成一个连通分量
        par[i]=i;
    long long res=0;
    sort(E,E+m);
    for(int i=0;i<m;i++){
        if(Same(E[i].from,E[i].to)) continue;
        join(E[i].from,E[i].to);
        res+=E[i].w;
    }
    return res;
}

int main(){
    while(scanf("%d%d",&n,&m)==2){
        for(int i=0;i<m;i++){
            scanf("%d%d%d",&E[i].from,&E[i].to,&E[i].w);
        }
        long long res=kruskal();
        for(int i=1;i<=n;i++){
            if(!Same(1,i)){
                res=-1;
                break;
            }
        }
        printf("%lld\n",res);
    }
    return 0;
}


利用qsort()排序。

#include<cstdio>
#include<stdlib.h>
using namespace std;
int parent[1001];
typedef struct Node{
    int u,v,info;
}Node;
 Node cla[3001];
 int cost;
int fnd(int t)
{
    int r=t;
    while(parent[r]!=r)
        r=parent[r];
    int i=t,j;
    while(i!=r)//压缩路径
    {
        j=parent[i];
        parent[i]=r;
        i=j;
    }
    return r;
}
void Kruskal(Node s)
{
    int fx=fnd(s.u);
    int fy=fnd(s.v);
    if(fx!=fy)
    {
        parent[fx]=fy;
        cost+=s.info;
       // printf("fx=%d,fy=%d,cost=%d\n",fx,fy,cost);
    }
}
int cmp(const void *a,const void *b)        //按村庄之间的预算成本从小到大排序
{
    return (*(Node *)a).info>(*(Node *)b).info?1:-1;
}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        int i,cnt=0;
        cost=0;
        for(i=1;i<=n;i++)
        {
            parent[i]=i;
        }
        for(i=0;i<m;i++)
        {
            scanf("%d%d%d",&cla[i].u,&cla[i].v,&cla[i].info);
        }
        qsort(cla,m,sizeof(cla[0]),cmp);    //qsort排序
        /*1 待排序数组首地址
          2 数组中待排序元素数量
          3 各元素的占用空间大小
          4 指向函数的指针,用于确定排序的顺序*/
          for(i=0;i<m;i++)
          {
              Kruskal(cla[i]);
          }
          for(i=1;i<=n;i++)
          {
              if(parent[i]==i)
                cnt++;
          }
          if(cnt!=1)
            printf("-1\n");
          else
            printf("%d\n",cost);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值