-
2 3 3 4 1 1 2 2 1 2 3 3 2 2 2 4 4 4 2 1 2 2 4 3 1 4 3 1 4 1 3
样例输出
-
Case #1: 4 Case #2: 13
描述
需要在一个N × M的网格中建立一个通讯基站,通讯基站仅必须建立在格点上。
网格中有A个用户,每个用户的通讯代价是用户到基站欧几里得距离的平方。
网格中还有B个通讯公司,维护基站的代价是基站到最近的一个通讯公司的路程(路程定义为曼哈顿距离)。
在网格中建立基站的总代价是用户通讯代价的总和加上维护基站的代价,最小总代价。
输入
第一行为一个整数T,表示数据组数。
每组数据第一行为四个整数:N, M, A, B。
接下来的A+B行每行两个整数x, y,代表一个坐标,前A行表示各用户的坐标,后B行表示各通讯公司的坐标。
输出
对于每组数据输出一行"Case #X: Y",X代表数据编号(从1开始),Y代表所求最小代价。
数据范围
1 ≤ T ≤ 20
1 ≤ x ≤ N
1 ≤ y ≤ M
1 ≤ B ≤ 100
小数据
1 ≤ N, M ≤ 100
1 ≤ A ≤ 100
大数据
1 ≤ N, M ≤ 107
1 ≤ A ≤ 1000
2.解题思路:本题要求找一个合适的位置建立一个基站,使得总费用最小。根据题意描述,费用包含两个部分,第一部分是用户到基站的欧几里得距离的平方,第二个是到最近的通讯公司的曼哈顿距离。不难想象,这个基站的位置应该大致处于n个用户中间的区域。其实,可以通过列式推导出最合适的位置的大致区域。
首先考虑用户的距离之和d1=sum{(x-xi)^2}+sum{(y-yi)^2},如果距离达到最小,那么该点一定是一个极小值点。根据高等数学的知识,该处的偏导数都为0,。因此可以事先对x,y求偏导。发现,x=sum{xi}/n,y=sum{yi}/n,因此可以对该点以及周围的8个点进行搜索。那么曼哈顿距离此时是否为最小呢?不难发现,曼哈顿距离的改变量每移动一格只会变化最多一个距离,但欧几里得距离最大的变化量会大于1个距离单位,因此应该尽量保证d1达到最小。
因此,只需要在平均值以及其周围的8个点中找出总距离最小的,就是最终的结果了。
3.代码:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<algorithm>
#include<string>
#include<sstream>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<functional>
using namespace std;
#define mxn 200005
#define LL long long
#define MP make_pair
#define REP(i, a, b) for (int i = a; i <= b; ++i)
#define FOR(i, a, b) for (int i = a; i < b; ++i)
int dx[] = { 0, 0, 0, 1, 1, 1, -1, -1, -1 };
int dy[] = { 0, 1, -1, 0, 1, -1, 0, 1, -1 };
LL ABS(LL x)
{
return x < 0 ? -x : x;
}
struct point
{
LL x, y;//考虑到大数据范围,选用long long
point(){};
point(LL x, LL y) :x(x), y(y){}
point operator - (const point& b) const //重载减号,方便输入
{
return point(x - b.x, y - b.y);
}
void input()
{
cin >> x >> y;
}
LL dis() //欧几里得距离的平方
{
return x * x + y * y;
}
LL len() //曼哈顿距离
{
return ABS(x) + ABS(y);
}
}A[1005], B[105];
LL cal(point o, int a, int b) //计算(a,b)点出的总距离
{
LL ret = ~0uLL >> 1;
REP(i, 1, b) ret = min(ret, (o - B[i]).len());
REP(i, 1, a) ret += (o - A[i]).dis();
return ret;
}
int main()
{
//freopen("t.txt", "r", stdin);
int t, n, m, a, b, cas = 0;
cin >> t;
while (t--)
{
cin >> n >> m >> a >> b;
REP(i, 1, a) A[i].input();
REP(i, 1, b) B[i].input();
LL ans = ~0uLL >> 1, x = 0, y = 0;
REP(i, 1, a) x += A[i].x, y += A[i].y;
x /= a, y /= a;//求平均值
FOR(k, 0, 9) //在平均值及其附近的8个点中找到最小距离
{
LL tx = x + dx[k], ty = y + dy[k];
if (tx < 1 || tx > n || ty < 1 || ty > m) continue;//出界
ans = min(ans, cal(point(tx, ty), a, b));
}
cout << "Case #" << ++cas << ": " << ans << endl;
}
return 0;
}