描述
需要在一个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
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
本题要求找一个合适的位置建立一个基站,使得总费用最小。根据题意描述,费用包含两个部分,第一部分是用户到基站的欧几里得距离的平方(xi-x0)^2+(yi-y0)^2,第二个是到最近的通讯公司的曼哈顿距离|xi-x0|+|yi-y0|。不难想象,这个基站的位置应该大致处于n个用户中间的区域。其实,可以通过列式推导出最合适的位置的大致区域。
首先考虑用户的距离之和d1=sum{(x-xi)^2}+sum{(y-yi)^2},如果距离达到最小,那么该点一定是一个极小值点。
该处的偏导数都为0。因此可以事先对x,y求偏导。发现 x=sum{xi}/n,y=sum{yi}/n,因此可以对该点以及周围的4个点进行搜索。那么曼哈顿距离此时是否为最小呢?不难发现,曼哈顿距离的改变量每移动一格只会变化最多一个距离,但欧几里得距离最大的变化量会大于1个距离单位,因此应该尽量保证d1达到最小。
因此,只需要在平均值以及其周围的4个点中找出总距离最小的,就是最终的结果了。
基站 “欧几里得距离的平方” 优先级远大于 “ 曼哈顿距离” 只需考虑欧几里德距离平方最小的坐标即可
欧几里德距离平方,x轴y轴可分离讨论。变成两个 “一维上到各点欧几里德平方和最小的点” ∑[(Xn-X0)2+(Yn-Y0)2]=∑(Xn-X0)2+∑(Yn-Y0)2
一维上到各点欧几里德平方和最小的点 =》 ∑(xn-x0)2 =Σ(Xn2)+nX0 *X0- 2X0 ∑(Xn) 最小
问题变成,求二元一次方程组最小解的横坐标
最后:
X0= ∑(Xn)/n;
Y0= ∑(Yn)/n;
因为要求 “通讯基站仅必须建立在格点上” 所以需要对最近的四个点进行判定(x+y) (x,y+1) (x+1,y) (x+1,y+1))
#include <iostream> #include <string.h> #include <stdlib.h> #include <math.h> using namespace std; long long totalAx=0,totalAxSquare=0; long long totalAy=0,totalAySquare=0; long long N,M,A,B; struct node { long long x,y; }P[100]; long long mins(long long a,long long b) { return a<=b?a:b; } long long ca(long long totalSquare,long long total,long long x,long long num) { return totalSquare+num*x*x-2*total*x; } long long cal(long long x,long long y) { long long ans=99999999; for(long long i=0;i<B;i++) ans=mins(ans,llabs(P[i].x-x)+llabs(P[i].y-y)); return ans+ca(totalAxSquare,totalAx,x,A)+ca(totalAySquare,totalAy,y,A); } int main() { int T,cases=0; cin>>T; while(T--) { cin>>N>>M>>A>>B; totalAx=0;totalAxSquare=0; totalAy=0;totalAySquare=0; for(int i=0;i<A;i++) { int x,y; cin>>x>>y; totalAx+=x; totalAxSquare+=x*x; totalAy+=y; totalAySquare+=y*y; } for(int i=0;i<B;i++) cin>>P[i].x>>P[i].y; long long x=totalAx/A,y=totalAy/A; long long ans=99999999; ans=mins(ans,cal(x,y)); ans=mins(ans,cal(x+1,y)); ans=mins(ans,cal(x,y+1)); ans=mins(ans,cal(x+1,y+1)); cout<<"Case #"<<++cases<<": "<<ans<<endl; } return 0; }