题目:
Meeting point-1
题意:给一群点,求所有点到某一点的距离之和的最小值,距离 = |x1-x2| + |y1-y2|
思路:直接枚举肯定超时。第一次做的时候是假定一维中位数定理在二维也成立,即:最小位置在中心的那群点(x,y接近中位数),然后x,y同时操作后超时了,于是改为仅对x排序,枚举中间n/2 - 300 , n/2 + 300的点,水过了。。。。。之后补题,用了预处理的方式,具体见代码注释
代码:
//#pragma comment(linker, "/STACK:102400000,102400000")
#include "iostream"
#include "cstring"
#include "algorithm"
#include "cmath"
#include "cstdio"
#include "sstream"
#include "queue"
#include "vector"
#include "string"
#include "stack"
#include "cstdlib"
#include "deque"
#include "fstream"
#include "map"
using namespace std;
typedef long long LL;
const long long INF = (long long)1e30;
const int MAXN = 100000+100;
#define eps 1e-14
const int mod = 100000007;
int n;
struct node
{
LL x,y;
LL id;
} p[MAXN]; //标号从1开始
LL sumx[MAXN],sumy[MAXN]; //sumx[i]表示从1到i的点的横坐标之和,sumy[i]同理,为纵坐标之和
LL minll(LL a , LL b)
{
if(a < b)
return a;
return b;
}
bool cmp1(node a, node b)
{
if(a.x == b.x)
return a.y < b.y;
return a.x < b.x;
}
bool cmp2(node a, node b)
{
if(a.y == b.y)
return a.x < b.x;
return a.y < b.y;
}
int main()
{
//freopen("in","r",stdin);
//freopen("out","w",stdout);
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d" , &n);
memset(sumx , 0 , sizeof(sumx));
memset(sumy , 0 , sizeof(sumy));
for(int i = 1 ; i <= n ; i++)
scanf("%I64d %I64d" , &p[i].x , &p[i].y);
sort(p+1,p+n+1,cmp1); //首先对x排序,预处理sumx,这样sumx[i]-sumx[j]表示从j+1~i的点的横坐标之和
//sumx[i]-sumx[j] - p[j].x*(i-j)就表示i~j之间的点到j点的距离
sumx[1] = p[1].x;
for(int i = 2 ; i <= n ; i++)
sumx[i] = sumx[i-1] + p[i].x;
sort(p+1,p+n+1,cmp2); //对y排序,并同样方法预处理sumy,由于之后我们还得按x排回来,所以此时记录一下某点在y
//方式排列时的位置:id
sumy[1] = p[1].y;
p[1].id = 1;
for(int i = 2 ; i <= n ; i++)
{
sumy[i] = sumy[i-1] + p[i].y;
p[i].id=i;
}
sort(p+1,p+n+1,cmp1);
LL minc = INF;
for(int i = 1 ; i <= n ; i++)
{
LL pre = (i)*p[i].x - sumx[i] //前i个点到i点的距离之和
+ (sumx[n] - sumx[i] - (n-i)*p[i].x); //后n-i个点到i点的距离之和
LL pos = p[i].id; //导出i点在y排列时的位置pos
pre += ((pos)*p[i].y-sumy[pos]) //同样的计算方法
+ (sumy[n] - sumy[pos] - (n-pos)*p[i].y);
minc = minll(minc , pre);
}
printf("%I64d\n",minc);
}
return 0;
}