題目:
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;
}