uva5713(次小生成树)

题意:

秦始皇要修建连通所有城市的路,一共有N个点以及各个点的坐标和人口。修建很多条路使N个城市连通,但是可以使其中一条路的修建费用为0。在修建费用最小的前提下,使得A/B最大。A指费用为0的路所连通的两个城市的人口和,B指除修建费用为0的路之外的所以路之和。

解题思路:因为要是整体的修建费用最小,必定要求他的最小生成树。但是可以使生成树中的一条边的费用为0,这样就不能简单的光求最小生成树。因为可以使连接某两个城市的路修建费用为0,所以可以枚举所有两个城市的组合。这样A的值就固定下来了,为了使得A/B最大。则需要删去这两个城市的连通路径中最长的边,使得B值越小。因为在这两个城市直接连边是不需要费用的。这样的话就和求次小生成树的算法有点像了。一开始把问题像的简单了,所以直接用了kruskal。但是求次小生成树还是prime算法比较方便。



这道题基本就是次小生成树的模板题,模板代码基本没怎么改

这里是关于次小生成树的介绍:http://blog.csdn.net/qq_31607947/article/details/77563793

看别人的代码蛮复杂,实际上就是 一次prim同时记录每两个点在树上的边中最大的那条,然后对于每两个点遍历,删边加边

double坑了我好几次wa

#include<iostream>

#include<stdio.h>

#include<string.h>

#include<algorithm>

#include<math.h>

using namespace std;

const int maxn = 1011;

const double inf = 0x3f3f3f3f;

double Map[maxn][maxn];

double Max[maxn][maxn];

int pre[maxn];

double dis[maxn];

int people[maxn],x[maxn],y[maxn];

bool vis[maxn];

double prim(int n)

{

    double ans = 0;

    memset(vis, false, sizeof(vis));

    memset(Max, 0, sizeof(Max));

    for (int i = 2; i <= n; i++)

    {

        dis[i] = Map[1][i];

        pre[i] = 1;

    }

    pre[1] = 0;

    dis[1] = 0;

    vis[1] = true;

    for (int i = 2; i <= n; i++)

    {

        double min_dis = inf;

        int k;

        for (int j = 1; j <= n; j++)

        {

            if (!vis[j] && min_dis > dis[j])

            {

                min_dis = dis[j];

                k = j;

            }

        }

        ans += min_dis;

        vis[k] = true;

        for (int j = 1; j <= n; j++)

        {

            if (vis[j]&&j!=k)

                Max[j][k] = Max[k][j] = max(Max[j][pre[k]], dis[k]);

            if (!vis[j] && dis[j] > Map[k][j])

            {

                dis[j] = Map[k][j];

                pre[j] = k;

            }

        }

    }

    return ans;

}

double smst(int n, double min_ans)

{

    double ans = 0;

    for (int i = 1; i <= n; i++)//枚举最小生成树之外的边

        for (int j = i + 1; j <= n; j++)

            ans = max(ans, (people[i]+people[j])/((min_ans-Max[i][j])*1.0));

    return ans;

}

int main()

{

    int T, n;

    scanf("%d", &T);

    while (T--)

    {

        scanf("%d",&n);

        for (int i = 1; i <=n; i++)

            scanf("%d %d %d", &x[i], &y[i], &people[i]);

        for (int i = 1;i<=n;i++)

            for(int j=i;j<=n;j++)

                Map[i][j]=Map[j][i]=sqrt(double((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]- y[j])));

        double ans = prim(n);

        printf("%.2lf\n",smst(n, ans));

    }

    return 0;

}

/*****************


本来准备放弃了回去没想到临走前改了double就过了

昨晚就睡了四个小时希望一会可以早早的睡着,最近失眠有点重



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值