编程之美 活动中心问题与三分法

楼主小弱一枚,发现在找工作中算法还是非常有用的。所以决心现在开始逐步学习。自己初赛都过不去,觉得看下这些题目还是蛮有意思的,总结了网上别人的方法,来写点自己的理解。


题目描述

时间限制: 12000ms
单点时限: 6000ms
内存限制: 256MB

描述

A市是一个高度规划的城市,但是科技高端发达的地方,居民们也不能忘记运动和锻炼,因此城市规划局在设计A市的时候也要考虑为居民们建造一个活动中心,方便居住在A市的居民们能随时开展运动,锻炼强健的身心。

城市规划局希望活动中心的位置满足以下条件:

1. 到所有居住地的总距离最小。

2. 为了方便活动中心的资源补给和其他器材的维护,活动中心必须建设在A市的主干道上。


为了简化问题,我们将A市摆在二维平面上,城市的主干道看作直角坐标系平的X轴,城市中所有的居住地都可以看成二维平面上的一个点。

现在,A市的城市规划局希望知道活动中心建在哪儿最好。


输入

第一行包括一个数T,表示数据的组数。

接下来包含T组数据,每组数据的第一行包括一个整数N,表示A市共有N处居住地

接下来N行表示每处居住地的坐标。


输出

对于每组数据,输出一行“Case X: Y”,其中X表示每组数据的编号(从1开始),Y表示活动中心的最优建造位置。我们建议你的输出保留Y到小数点后6位或以上,任何与标准答案的绝对误差或者相对误差在10-6以内的结果都将被视为正确。


数据范围

小数据:1 ≤ T ≤ 1000, 1 ≤ N ≤ 10

大数据:1 ≤ T ≤ 10, 1 ≤ N ≤ 105

对于所有数据,坐标值都是整数且绝对值都不超过106



样例解释

样例1:活动中心的最优建造位置为(1.678787, 0)


样例输入
1
3
1 1
2 2
3 3
样例输出
Case 1: 1.678787

解法

网上一搜,很多人说用三分法解,具体为什么,估计各类大牛都不懈于细说,刚好我有时间写个详细解题过程。

我们一步步来看这个问题。

假设平面上的N个点分别为(x1,y1),(x2,y2),..., (xn,yn),为方便计算,不妨设定x1<=x2<=....<=xn。

题目所求,即在x轴上找一点(x,0),到所有点的距离和最小。距离可以如下计算:


问题转化为求该式的最小值。

如果可以直接找到其解析解最好了,不过看起来不容易。

没有好的窍门,所以对s(x)求导:


当x=x1时,函数s'(x)<=0。当x=xn时,s'(x)>=0。如果s'(x)单增,则导函数由负到正,s(x)先减后增,刚好有一个凹点,且位于区间[x1,xn]上。

对s'(x)再次求导:


验证了刚刚的设想,s''(x)>=0,因而s'(x)单增。所以在[x1,xn]之间有一个极小值点,求解该极小值即可。

对于单调的函数,可以用二分法查找某个解。对于凹凸性函数,求解极值可以采取三分法。




如图是求解凸函数极值点的一个示意图。mid=(Left+Right)/2, midmid=(mid+Right)/2。每次选定mid和midmid后比较大小,

如果value[mid] > value[midmid],极值点必定位于直线y=value[midmid]以上部分。可以排除区间[midmid,Right],所以令Right=midmid。

反之,如果value[mid] < value[midmid],极值点必定位于直线y=value[mid]以上部分。可以排除区间[Left, mid],所以令Left=mid。

重复上述过程,直到满足要求精度为止。

附上一个网上找来的代码模板:

double Calc(Type a)
{
    /* 根据题目的意思计算 */
}
void Solve(void)
{
    double Left, Right;
    double mid, midmid;
    double mid_value, midmid_value;
    Left = MIN; Right = MAX;
    while (Left + EPS < Right)
    {
        mid = (Left + Right) / 2;
        midmid = (mid + Right) / 2;
        mid_area = Calc(mid);
        midmid_area = Calc(midmid);
        // 假设求解最大极值.
        if (mid_area >= midmid_area) Right = midmid;
        else Left = mid;
    }
}

本题是求解凹函数极小值的,原理类似,至此不再详述。

本文介绍凸函数部分及代码模板,来自该文:http://hi.baidu.com/czyuan_acm/item/81b21d1910ea729c99ce33db

欢迎大家探讨!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值