HDU 4463 Outlets(最小生成树)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4463

题意:要在n个商店之间修路,商店p和q必须直接通过一条马路相连,所有商店之间必须能通过马路联系,求修建马路的最小长度。

分析:其实就是简单的求最小生成树问题,不过加了一个限制条件那就是点p q必须连接在一起,可在用并查集求最小生成树之前先将pq合并到一个子集中,然后在按部就班的求即可,最后一定形成了(n-1)条边,输出这(n-1)条边的长度之和即可。

反思:比赛时近30个人都写出了这道题,而我是比完赛向美丽的zj学长请教时才知道他要求的是最小生成树,可用优先队列加并查集快速求解。当我问他什么是最小生成树,什么是并查集,为什么要用优先队列时,我强烈感觉到他头顶飞过了不止n只乌鸦。为自己的无知汗颜。但是现在已经弄得基本熟悉了,我相信遇到类似算法我应该也能解决了。人生就是一个从无知到慢慢懂得一些事情,懂得多了又变得迷茫感到无知然后继续丰富充实自我的过程嘛,在这条路上我会努力前行的,因为我有要守护的人,我要成为爷爷奶奶最大的骄傲与依靠,cx加油!!!

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<map>
#include<vector>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn = 10000 + 5;
struct node
{
    int x,y;
}no[55];
double dis(node n1, node n2)//计算两点之间的距离
{
    return sqrt(double(n1.x - n2.x)*(n1.x - n2.x) + double(n1.y - n2.y)*(n1.y - n2.y));
}
struct edge//定义边结构体储存起点 终点 及边的长度
{
    int s,e;
    double d;
    edge(int s, int e, double d):s(s), e(e), d(d){}
    bool operator<(const edge & ed) const
    { return d > ed.d; }

};
priority_queue<edge> pq;//利用优先队列将所有边按长度从小到大排好序,每次先取长度小的边
int f[55];
int Find(int x)
{
    return x == f[x] ? x : f[x] = Find(f[x]);//寻找一个点的父节点
}
bool Union(int x, int y)//判断两点是否能合并
{
    int fx = Find(x);
    int fy = Find(y);
    if(fx == fy) return false;//父节点相同,若合并则会形成圈,这样就不是最小生成树了,故不能合并
    f[fx] = fy;//父节点不同,将父节点合并,返回真
    return true;
}
int main()
{
    int n;
    while(~scanf("%d",&n) && n)
    {
        while(!pq.empty()) pq.pop();
        memset(no, 0, sizeof(no));
        for(int i = 1; i <= n; i++)
            f[i] = i;//合并前每个点的父节点都是它本身
        int p,q;
        scanf("%d%d",&p,&q);
        for(int i = 1; i <= n; i++)
        {
            scanf("%d%d",&no[i].x, &no[i].y);
        }
        for(int i = 1; i <= n; i++)
            for(int j = i + 1; j <= n; j++)
            {
                pq.push(edge(i, j, dis(no[i], no[j])));//将各边加入优先队列中优先队列会自动排序
            }
        Union(p,q);//合并必须直接相连的p q两点
        double sum = dis(no[p], no[q]);
        while(!pq.empty())//依次从优先队列中拿出各边,若改变的两个端点能合并,则合并,否则看下一条边
        {
            int x = pq.top().s, y = pq.top().e;
            double di = pq.top().d;
            pq.pop();
            if(Union(x, y))
                sum += di;
        }
        printf("%.2lf\n",sum);//最小生成树最终一定是(n-1)条边
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值