HDU 1173 采矿(思路)

Problem Description
某天gameboy玩魔兽RPG。有一个任务是在一个富含金矿的圆形小岛上建一个基地,以最快的速度采集完这个小岛上的所有金矿。这个小岛上有n(0<n<1000000)个金矿,每个金矿的矿藏量是相等的。而且这个小岛的地势非常平坦,所以基地可以建在小岛的任何位置,每个金矿的采矿速度只跟矿藏到基地的路程长度有关。为了不让这个任务太无聊,游戏设计者对这个小岛施了个“魔法”,规定矿工在小岛上只能正南正北正西正东走。也就是说矿工不能斜着在岛上走。

这个小岛在一个二维直角坐标系中描述。

你的任务就是帮gameboy找一个建造基地的位置,使矿工能以最快的速度采完所有矿。
 

Input
输入数据有多组。每组数据的第一行是一个正整数n(0<n<1000000),表示小岛上有n个金矿。在接下来的n行中,每行有两个实数x,y,表示其中一个金矿的坐标。n=0表示输入数据结束。
 

Output
每一组输入数据对应一行输出,输出两个实数x,y(保留小数点后两位),也就是你找到的建造基地的位置坐标。如果坐标不唯一,可以任选一个输出。
 

Sample Input
 
 
41.0 1.03.0 1.03.0 3.01.0 3.00
 

Sample Output
 
 
2.00 2.00

题意:

给出n个笛卡尔坐标系上的点,找到一点(可以不在n个点中)在x,y轴上投影,到其他所有点在x,y轴上投影,距离和最短


思路:

先将其转化为2个,求1维最短距离和的子问题。

刚开始以为是平均数,后来发现平均数并不是最优解,如1,5,6。


下面是一维上答案是中位数的证明(伪

设金矿有n个,每个金矿在ai 点( ai < a(i+1) )

n == 1 时,答案在a1点

n == 2 时,答案在区间[a1,a2]上

n  >  2 时, 答案在 [a1,an],[a2,an-1]……上,所以答案是中间的点,或线段上。


--------------------------------------------------------------------------------------------------------------


另一种写法 和 伪证明

证明前提是已知点上必定存在答案,这个不会证明,凭直觉。

dp求每点到所有点距离和

//a为坐标点,dpa为a到其余点距离和
for(int i = 1; i < n; i++)
{
    dpa[i] = dpa[i-1] + (a[i] - a[i-1])*i - (a[i] - a[i-1])*(n-i);
}

不难发现dpa是一个先减后增的数列,i>(n-i)时增,i<(n-i)时减。

这应该也可以说明答案是中位数吧。。。

--------------------------------------------------------------------------------------------------------------

代码:

直接求解:

#include<stdio.h>
#include<algorithm>
using namespace std;

double a[1000005], b[1000005];

int main()
{
    int n;
    while(scanf("%d",&n), n)
    {
        for(int i = 0; i < n; i++) scanf("%lf%lf",&a[i],&b[i]);
        sort(a,a+n);
        sort(b,b+n);
        printf("%.2lf %.2lf\n",a[(n-1)/2], b[(n-1)/2]);
    }
    return 0;
}

dp:

#include<stdio.h>
#include<algorithm>
using namespace std;

double a[1000005], b[1000005], dpa[1000005], dpb[1000005];

int main()
{
    int n;
//    freopen("F:\\my.txt","w",stdout);
    while(scanf("%d",&n), n)
    {
        for(int i = 0; i < n; i++) scanf("%lf%lf",&a[i],&b[i]);

        sort(a,a+n);
        sort(b,b+n);

        for(int i = 1; i < n; i++)
        {
            dpa[0] = a[i] - a[0];
            dpb[0] = b[i] - b[0];
        }


        for(int i = 1; i < n; i++)
        {
            dpa[i] = dpa[i-1] + (a[i] - a[i-1])*i - (a[i] - a[i-1])*(n-i);

            dpb[i] = dpb[i-1] + (b[i] - b[i-1])*i - (b[i] - b[i-1])*(n-i);
        }

        int ans1 = 0, ans2 = 0;

        for(int i = 1; i < n; i++)
        {
            if(dpa[i] < dpa[ans1]) ans1 = i;
            if(dpb[i] < dpb[ans2]) ans2 = i;
        }
        printf("%.2lf %.2lf\n",a[ans1], b[ans2]);
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值