【贪心】畅通工程系列4-HDU1233 还是畅通工程-Prim算法

题目

http://acm.hdu.edu.cn/showproblem.php?pid=1233

问题分析

本题求是整个省畅通的最低成本,把村庄作为顶点,把公路作为边,那么本题就是求最小生成树的总边权值。

算法

算法核心

本题可采用Prim算法,其实这也是贪心的一种。设V为顶点的全集,U初始化{v0},每次都选择V-U中到分支U最近的点,然后把该点加入U,更新V-U中的点到U的距离,然后再次选择直至U=V。

算法流程

closest[]:V-U中的点到分支U的最近的点;
lowcost[]:V-U中的点到分支U的最短距离,也就是map_[j][closest[j]];
vis[]:是否访问,即是否在U中;
本题要求的是最小生成树,即使整个图连通且无圈的边权值的最小值,所以以哪个顶点为起始点都可以,不会影响最后的结果,我们就以V1为起始点为例。首先将closest[]数组初始化为1,U为{v1},即V-U中的顶点均与U中的v1离得最近,lowcost[i]也初始化为map_[i][1];然后遍历找到V-U中lowcost[]的最小值k,把该点加入U,即vis[k]=1;之后更新V-U的lowcost[]数组,如果到vk的距离小于原来的距离,就更新lowcost[],更新closest[];直至最后V-U为空集,然后遍历将lowcost[]中的值相加即为所求。

一点想法

求最小边权值一定是最后遍历lowcost[]数组,而不是像kruskal算法选一条边算一遍,因为在选择一个顶点加入U之后,可能会更新很多条边的值,但是下一步只会选择一条边,不知道更新的哪一条边会作为最小生成树的其中一边,所以无法在循环选顶点的循环中计算。

WA的教训

一定要分清边数和顶点数,尤其是循环比较多的时候,不然可能会像我一样惨兮兮的RE好几遍。。。。

代码实现

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn=105;
const int INF=1<<29;

int n,m;//顶点数,边数
int closest[maxn];//V-U中离U最近的顶点
int lowcost[maxn];//V-U中顶点到U的最小边权值
int vis[maxn];//是否在U中
int map_[maxn][maxn];
int ans;//最低成本

void init()
{
    for(int i=1; i<=n; ++i)
    {
        closest[i]=1;
        lowcost[i]=map_[i][1];
    }
    memset(vis,0,sizeof(vis));
    ans=0;
    vis[1]=1;
    lowcost[1]=0;
}

void Prim()
{
    init();
    int i,j,k,min_num;
    for(i=1; i<=n; ++i)
    {
        min_num=INF;
        k=0;
        for(j=1; j<=n; ++j)//找到V-U中离U最近的顶点
            if(!vis[j]&&lowcost[j]<min_num)
            {
                k=j;
                min_num=lowcost[j];
            }
        if(k==0)//V-U为空说明算法结束
            break;
        vis[k]=1;
        for(j=1; j<=n; ++j)//更新lowcost[]和closest[]
            if(!vis[j]&&map_[j][k]<lowcost[j])
            {
                lowcost[j]=map_[j][k];
                closest[j]=k;
            }
    }
}

int main()
{
    int i,j,a,b,v;
    while(scanf("%d",&n)==1&&n)
    {
        m=n*(n-1)/2;
        memset(map_,0,sizeof(map_));
        for(i=0; i<m; ++i)
        {
            scanf("%d%d%d",&a,&b,&v);
            map_[a][b]=v;
            map_[b][a]=v;
        }
        Prim();
        for(i=1; i<=n; ++i)
            ans+=lowcost[i];
        printf("%d\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值