-
描述
-
一个街区有很多住户,街区的街道只能为东西、南北两种方向。
住户只可以沿着街道行走。
各个街道之间的间隔相等。
用(x,y)来表示住户坐在的街区。
例如(4,20),表示用户在东西方向第4个街道,南北方向第20个街道。
现在要建一个邮局,使得各个住户到邮局的距离之和最少。
求现在这个邮局应该建在那个地方使得所有住户距离之和最小;
-
输入
-
第一行一个整数n<20,表示有n组测试数据,下面是n组数据;
每组第一行一个整数m<20,表示本组有m个住户,下面的m行每行有两个整数0<x,y<100,表示某个用户所在街区的坐标。
m行后是新一组的数据;
输出
- 每组数据输出到邮局最小的距离和,回车结束; 样例输入
-
2 3 1 1 2 1 1 2 5 2 9 5 20 11 9 1 1 1 20
样例输出
-
2 44
来源
- 经典题目 上传者
- iphxer
- 思路:注意不一定在某个居民点上,但一定在中间。
- 题目的意思实际上就是让我们求所有点的x到邮局x之和加上所有点的y到邮局y之和的和。
- 因为距离只计算x轴和Y轴距离之和而不是斜对角线距离,所以可以把x轴距离和y轴距离分开分析计算最短距离,彼此一定不会影响。
-
以x轴横向点到其他点最短距离为例:问题等价于在X轴有若干点中选取其中一点,使该点到其他点距离和最小。
这个最短距离点一定是排在中间的那个点,比如有5个点,一定选择第3个点;如果有4个点,一定选择第2个点或者第3个点(可以证明选择这两个点任意一个,结果一定相同)
选择中间点的反证法证明:
假设选择非中间点,并认为该点到其他点的距离和最小,大家可以在X轴上画画图算算,一定还可以找到距离和比最小值还小一段距离的点(距离大小为假设最小点到中间点的距离),这和我们假设距离和最小条件矛盾,所以只能选中间点。 - 方法一:暴力:
-
#include <iostream> #include <algorithm> using namespace std; const int INF = 0x3f3f3f3f; int main(){ int n, m; int a[23], b[23]; cin >> n; while(n--){ cin >> m; for(int i = 0; i < m; i++){ cin >> a[i] >> b[i]; } int ans = INF, sum = 0; // for(int i = 0; i < m; i++){ //遍历所有用户的点 // sum = 0; // for(int j = 0; j < m; j++){ // sum += abs(a[i] - a[j]) + abs(b[i] - b[j]); // } // ans = min(ans, sum); // } for(int i = 0; i < 100; i++){ //遍历所有可能区间内的点,上面的代码不能通过,可见不一定落在用户位置上 for(int j = 0; j < 100; j++){ sum = 0; for(int k = 0; k < m; k++) sum += abs(i - a[k]) + abs(j - b[k]); ans = min(ans, sum); } } cout << ans << endl; } return 0; } /* 3 8 1 1 2 1 3 1 1 2 3 2 1 3 2 3 3 3 */
方法二:找中点
#include <iostream> #include <algorithm> using namespace std; int main(){ int a[22], b[22]; int t; cin >> t; while(t--){ int m; cin >> m; for(int i = 0; i < m; i++){ cin >> a[i] >> b[i]; } sort(a, a + m); sort(b, b + m); int ax = a[m / 2]; //ax为所有x排好序的中间那个 int by = b[m / 2]; int ans = 0; for(int i = 0; i < m; i++) ans += abs(ax - a[i]) + abs(by - b[i]); cout << ans << endl; } return 0; }