楼主小弱一枚,发现在找工作中算法还是非常有用的。所以决心现在开始逐步学习。自己初赛都过不去,觉得看下这些题目还是蛮有意思的,总结了网上别人的方法,来写点自己的理解。
题目描述
-
1 3 1 1 2 2 3 3
样例输出
-
Case 1: 1.678787
描述
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)
解法
网上一搜,很多人说用三分法解,具体为什么,估计各类大牛都不懈于细说,刚好我有时间写个详细解题过程。
我们一步步来看这个问题。
假设平面上的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
欢迎大家探讨!