HDU-#1102 Constructing Roads(Prim & Kruskal)

题目大意:给定n个村庄的距离关系,以及已经连接的路径,以最小的距离连接其它村庄,还需要最小距离为多少?

解题思路:最小生成树的应用,prim的写法是对于以及连接好的我们把权值置为0,这样就能保证连接好的边在最小生成树里。然后记录最小生成树边之和即可。类似于Dijkstra算法的最基础写法。详见code。

题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=1102

Prim code:

<pre name="code" class="cpp">#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int MAXN =100+10;
const int INF = 0x3fffffff;
int map[MAXN][MAXN],vis[MAXN],dist[MAXN];
int n,q,a,b;

int Prim(){
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=n;i++) dist[i]=(i==1?0:map[1][i]); //初始化
    int sum=0;
    for(int i=1;i<=n;i++){
        int pos,minn=INF;
        for(int j=1;j<=n;j++) if(!vis[j] && minn>dist[j]) minn=dist[pos=j];  //选出dist值最小的结点pos
        vis[pos]=1;  //标记结点
        sum+=minn; //记录最小值的和
        for(int k=1;k<=n;k++){
            if(!vis[k] && dist[k]>map[pos][k])  //更新最小值
                dist[k]=map[pos][k];
        }
    }
    return sum;
}

int main(){
    while(scanf("%d",&n)!=EOF){
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                scanf("%d",&map[i][j]);
        scanf("%d",&q);
        for(int i=1;i<=q;i++){  //如果已经连接则置为0,保证是最小生成树的边
                scanf("%d%d",&a,&b);
                map[a][b]=map[b][a]=0;
            }
        int ans=Prim();
        printf("%d\n",ans);
    }
    return 0;
}

 

再补一个用并查集写的Kruskal。

Kruskal code:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int MAXN = 100+10;
int n,q,a,b,tmp;
int p[MAXN],map[MAXN][MAXN];
struct edge{
    int u,v,w;
}e[MAXN*MAXN];


int cmp(const edge a,const edge b){return a.w<b.w;}  //间接排序函数
int find(int x){return p[x]==x?x:p[x]=find(p[x]);}  //并查集的find函数

int Kruskal(){
    int ans=0;
    for(int i=0;i<tmp;i++){
        int x=find(e[i].u);//找到当前边的两个端点的集合
        int y=find(e[i].v);
        if(x!=y){//不在同一个集合则合并,记录该边的权值
            p[x]=y;
            ans+=e[i].w;
        }
    }
    return ans;
}

int main(){
    while(scanf("%d",&n)!=EOF){
        tmp=0;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                scanf("%d",&map[i][j]);
        for(int i=1;i<=n;i++)
            for(int j=1;j<i;j++){//因为是单向的,所以只记录左下角部分
                e[tmp].u=i;
                e[tmp].v=j;
                e[tmp++].w=map[i][j];
            }
        sort(e,e+tmp,cmp); //排序
        for(int i=0;i<=n;i++) p[i]=i;  //初始化并查集
        scanf("%d",&q);
        for(int i=0;i<q;i++){
            scanf("%d%d",&a,&b);
            int x=find(a);//找到当前边的两个端点的集合
            int y=find(b);
            if(x!=y) p[x]=y;  //不在同一个集合则合并
        }
        int ans=Kruskal();
        printf("%d\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值