[HDU4081]Qin Shi Huang's National Road System

秦始皇的国家道路系统

传送门HDU4081

Description

(略去长段对秦始皇的介绍…)
Qin Shi Huang undertook gigantic projects, including the first version of the Great Wall of China, the now famous city-sized mausoleum guarded by a life-sized Terracotta Army, and a massive national road system. There is a story about the road system:
There were n cities in China and Qin Shi Huang wanted them all be connected by n-1 roads, in order that he could go to every city from the capital city Xianyang.
Although Qin Shi Huang was a tyrant, he wanted the total length of all roads to be minimum,so that the road system may not cost too many people’s life. A daoshi (some kind of monk) named Xu Fu told Qin Shi Huang that he could build a road by magic and that magic road would cost no money and no labor. But Xu Fu could only build ONE magic road for Qin Shi Huang. So Qin Shi Huang had to decide where to build the magic road. Qin Shi Huang wanted the total length of all none magic roads to be as small as possible, but Xu Fu wanted the magic road to benefit as many people as possible —- So Qin Shi Huang decided that the value of A/B (the ratio of A to B) must be the maximum, which A is the total population of the two cites connected by the magic road, and B is the total length of none magic roads.
Would you help Qin Shi Huang?
A city can be considered as a point, and a road can be considered as a line segment connecting two points.
有n个城市,秦始皇要修用n-1条路把它们连起来,要求从任一点出发,都可以到达其它的任意点。秦始皇希望这所有n-1条路长度之和最短。然后徐福突然有冒出来,说是他有魔法,可以不用人力、财力就变出其中任意一条路出来。

秦始皇希望徐福能把要修的n-1条路中最长的那条变出来,但是徐福希望能把要求的人力数量最多的那条变出来。对于每条路所需要的人力,是指这条路连接的两个城市的人数之和。

最终,秦始皇给出了一个公式,A/B,A是指要徐福用魔法变出的那条路所需人力, B是指除了徐福变出来的那条之外的所有n-2条路径长度之和,选使得A/B值最大的那条。

希望A/B的值最大,那么最大是多少?

Input

The first line contains an integer t meaning that there are t test cases(t <= 10).
For each test case:
The first line is an integer n meaning that there are n cities(2 < n <= 1000).
Then n lines follow. Each line contains three integers X, Y and P ( 0 <= X, Y <= 1000, 0 < P < 100000). (X, Y) is the coordinate of a city and P is the population of that city.
It is guaranteed that each city has a distinct location.

Output

For each test case, print a line indicating the above mentioned maximum ratio A/B. The result should be rounded to 2 digits after decimal point.

Sample Input

2
4
1 1 20
1 2 30
200 2 80
200 1 100
3
1 1 20
1 2 30
2 2 40

Sample Output

65.00
70.00

Solution

  • 按照修路的要求,构成了一棵最小生成树
  • 假定魔法道路确定,则路径总和是一定的,因此要在这基础上选择最大人数
  • 在最小生成树上任意割去一条道路,此时所有城市分为两个点集,分别查找到人数最多的城市,将这两个城市直接相连,比较答案即可
  • 可以看到n个城市组成的是一个无向完全图,任意两点间都有直接道路相连,所以可以保证一定可以将两个点集重新连接
  • 实现技巧方面,用prim求最小生成树,默认根为1,重新建图。枚举割边的时候只需要枚举n个点,将其与父亲节点的连边割去

Code

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
struct JOKER{
    int b,next;
}e[2000007];
struct CITY{
    double x,y,p;
}c[1007];
int cas,n,tot,fa[1007],head[1007];
double ans,ANS,a[1007][1007],dis[1007];
bool vis[1007],group[1007];
double dist(int x1,int y1,int x2,int y2){
    return sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
}
void add(int x,int y){
    e[++tot].b=y;
    e[tot].next=head[x];
    head[x]=tot;
}
void dfs(int u,int x){
    //printf("u=%d\n",u);
    group[u]=1;
    for (int i=head[u];i;i=e[i].next){
        int v=e[i].b;
        if (v==x||vis[v]) continue;
        vis[v]=1;
        dfs(v,x);
    }
}
int main(){
    scanf("%d",&cas);
    while (cas--){
        scanf("%d",&n);
        ANS=ans=0.0;
        for (int i=1;i<=n;i++) 
            scanf("%lf%lf%lf",&c[i].x,&c[i].y,&c[i].p);
        for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
            a[i][j]=dist(c[i].x,c[i].y,c[j].x,c[j].y);

        //prim
        memset(vis,0,sizeof(vis));
        memset(dis,0,sizeof(dis));
        memset(fa,0,sizeof(fa));
        for (int i=1;i<=n;i++) dis[i]=1e9+7;
        int m=n; dis[1]=0;
        while (m--){
            int v;
            double minn=1e9+7;
            for (int i=1;i<=n;i++)
                if (vis[i]==0&&dis[i]<minn){minn=dis[i]; v=i;}
            vis[v]=1; ans+=minn;
            for (int i=1;i<=n;i++)
                if (!vis[i]&&a[v][i]<dis[i]){
                    dis[i]=a[v][i];
                    fa[i]=v;
                } 
        }
        memset(head,0,sizeof(head)); tot=0;
        for (int i=2;i<=n;i++){
            add(i,fa[i]); add(fa[i],i);
        }
        for (int i=2;i<=n;i++){
            for (int j=1;j<=n;j++){group[j]=0; vis[j]=0;}
            vis[1]=1;
            dfs(1,i);
            double x=0,y=0;
            for (int j=1;j<=n;j++)
                if (group[j]) x=max(x,c[j].p);
                    else y=max(y,c[j].p);
            //printf("%0.2f %0.2f\n",x,y);
            ANS=max(ANS,(x+y)/(ans-a[fa[i]][i]));
        }
        printf("%0.2f\n",ANS);
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值