JZOJ3256 【TJOI2013】松鼠聚会 切比雪夫距离转曼哈顿距离

题目大意

给在给你二维平面上的 n 个点(xi,yi),现在规定从一个格点走到八相邻的格点的费用是1。现在要求你选出其中一个给定的点,要求其余所有点到这个点的花费和最小。

n105
xi,yi109

解题思路

首先先分析一下怎样能更简便的算费用,不难发现,由于每走一步可以是横坐标距离减1或纵坐标距离减1,或两个同时减1。那么从一个点走到另一个点的代价显然是 max(|x1x2|,|y1y2|) 。这个距离的定义有一种更好听的名字——切比雪夫距离。

但是由于同时存在绝对值和取 max ,很难处理,但是众所周知,假如是求曼哈顿距离,只需把横坐标和纵坐标分开讨论,然后对每个排一下序,分别求大于某个值和小于某个值的贡献就行了。

所以我们考虑转成曼哈顿距离来解决此题。

切比雪夫距离转曼哈顿距离

考虑转化一下求切比雪夫距离的公式:

max(|x1x2|,|y1y2|)=max(x1x2,x2x1,y1y2,y2y1)

再考虑转化一下曼哈顿距离的公式:
|x1x2|+|y1y2|=max(x1x2+y1y2x2x1+y1y2,x1x2+y2y1,x2x1+y2y1)

我们发现新的求曼哈顿距离的式子和求切比雪夫距离的式子长的很像,那么我们尝试一下重新定义一下坐标,是这两个式子变得相等。

x3=x1+y1,y3=x1y1,x4=x2+y2,y4=x2y2 。把 x3,y3,x4,y4 重新代入到切比雪夫距离式子中表示 x1,y2 ,我们就可以发现它的形式就变成了:

x1=y3+x32,y1=y3x32x2=y4+x42,y2=y4x42=max(x3x4+y3y4x4x3+y3y4,x3x4+y4y3,x4x3+y4y3)2

这跟曼哈顿距离的求算公式几乎一样,那么我们只需求 (x3,y3)(x4,y4) 的曼哈顿距离再除 2 就是(x1,y1),(x2,y2)的切比雪夫距离。而曼哈顿距离很好解决,那么这题也就就解决了。

程序

//YxuanwKeith
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;
typedef long long LL;

const int MAXN = 1e5 + 5;
const int Inf = 1e9 + 7;

struct Node {
    int x, y;
    Node(int a, int b) {x = a, y = b;}
    Node() {}
};

Node P[MAXN];
int n, Ox[MAXN], Oy[MAXN], Belx[MAXN], Bely[MAXN];
LL Sufx[MAXN], Sufy[MAXN], Prex[MAXN], Prey[MAXN];

bool CmpX(int A, int B) {
    return P[A].x < P[B].x;
}

bool CmpY(int A, int B) {
    return P[A].y < P[B].y;
}

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i ++) {
        int x, y;
        scanf("%d%d", &x, &y);
        P[i] = Node(x + y, x - y);
    }
    for (int i = 1; i <= n; i ++) Ox[i] = Oy[i] = i;
    sort(Ox + 1, Ox + 1 + n, CmpX);
    sort(Oy + 1, Oy + 1 + n, CmpY);
    for (int i = 1; i <= n; i ++) Belx[Ox[i]] = i;
    for (int i = 1; i <= n; i ++) Bely[Oy[i]] = i;
    for (int i = 1; i <= n; i ++) Prex[i] = Prex[i - 1] + P[Ox[i]].x;
    for (int i = 1; i <= n; i ++) Prey[i] = Prey[i - 1] + P[Oy[i]].y;
    for (int i = n; i; i --) Sufx[i] = Sufx[i + 1] + P[Ox[i]].x;
    for (int i = n; i; i --) Sufy[i] = Sufy[i + 1] + P[Oy[i]].y;
    LL Ans = 1ll << 62;
    for (int i = 1; i <= n; i ++) {
        int Sx = Belx[i]; 
        LL Sumx = 1ll * Sx * P[i].x - Prex[Sx] + Sufx[Sx + 1] - 1ll * (n - Sx) * P[i].x;
        int Sy = Bely[i];
        LL Sumy = 1ll * Sy * P[i].y - Prey[Sy] + Sufy[Sy + 1] - 1ll * (n - Sy) * P[i].y;
        if (Sumx + Sumy < Ans) Ans = Sumx + Sumy;
    }
    printf("%lld\n", Ans / 2);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值