[绝对值不等式] 货仓选址(绝对值不等式+贪心)

0. 前言

最最最经典的绝对值不等式问题,有很多变种。这个题是最裸的模板题了。

1. 排序不等式+贪心

104. 货仓选址

在这里插入图片描述

贪心思路:

  • 按照商店坐标从小到大排序,选择中位数

证明:

  • 假设商店已经按照从小到大排序,记为 x 1 , x 2 , x 3 , . . . , x n x_1, x_2,x_3,...,x_n x1,x2,x3,...,xn,货仓在 x x x 位置。那么可计算得到总的距离之和为 f ( x ) = ∣ x 1 − x ∣ + ∣ x 2 − x ∣ + . . . + ∣ x n − x ∣ + f(x)=\mid x_1 -x\mid+\mid x_2 -x\mid+...+\mid x_n -x\mid+ f(x)=x1x+x2x+...+xnx+简单调整,得: f ( x ) = ( ∣ x 1 − x ∣ + ∣ x n − x ∣ ) + ( ∣ x 2 − x ∣ + ∣ x n − 1 − x ∣ ) + . . . f(x)=(\mid x_1 -x\mid+\mid x_n -x\mid)+(\mid x_2 -x\mid+\mid x_{n-1} -x\mid)+... f(x)=(x1x+xnx)+(x2x+xn1x)+... 由简单的高中数学知识可知, ∣ a − x ∣ + ∣ b − x ∣ \mid a -x\mid+\mid b-x\mid ax+bx 若求其最小值,可以数形结合转化为数轴上有点 a 、 b a、b ab,再给一点 x x x,求 x x x a 、 b a、b ab 点的距离之和。则 x x x 可能出现在 a 、 b a、b ab 点两侧和中间,总共三种位置关系。其中出现在中间的时候距离之和最小,即为 ∣ a − b ∣ \mid a-b\mid ab 。则简单放缩有: f ( x ) = ( ∣ x 1 − x ∣ + ∣ x n − x ∣ ) + ( ∣ x 2 − x ∣ + ∣ x n − 1 − x ∣ ) + . . . f(x)=(\mid x_1 -x\mid+\mid x_n -x\mid)+(\mid x_2 -x\mid+\mid x_{n-1} -x\mid)+... f(x)=(x1x+xnx)+(x2x+xn1x)+... ≥ ( x n − x 1 ) + ( x n − 1 − x 2 ) . . . \ge (x_n-x_1)+(x_{n-1}-x_2)... (xnx1)+(xn1x2)...等号成立的条件是中位数 x x x 在两点之间,针对所有绝对值不等式等号成立
  • 如果是奇数个商店,则排序后直接取到中位数,数组下标从 0 开始,则为 n − 1 2 \frac {n - 1} 2 2n1,或者 C++ 除法自动向下取整,则 n 2 \frac {n} 2 2n
  • 如果是偶数个商店,排序后取到左中位数与右中位数均可 n − 1 2 \frac {n - 1} 2 2n1 或者 n 2 \frac {n} 2 2n
  • 统一起来就是, n 2 \frac {n} 2 2n。千万别写成 n 2 − 1 \frac {n} 2 - 1 2n1

中位数公式: m i n ∑ i n ∣ A − x i ∣ min\sum_i^n\mid A-x_i\mid mininAxi 则, A A A x i x_i xi 的中位数

本题可以拓展为二维求曼哈顿距离可以排序来做,但若是欧几里得距离则是一个经典求费马点的问题,采用的是随机算法,没有一般的优秀求法。

关于这个问题的证明,出了采用绝对值不等式来进行公式证明,还有微扰法可以证明。

abs(a[i] - a[n >> 1]) 改为 abs(a[i] - a[i >> 1]) 也可以 AC。可以证明 ∑ i = 1 n a i − a n 2 = ∑ i = 1 n a i − a i 2 \sum_{i=1}^n a_i-a_\frac n 2=\sum_{i=1}^na_i-a_\frac i 2 i=1naia2n=i=1naia2i

贴一份来自大佬的证明(奇数情况,偶数情况同理可证):
在这里插入图片描述

代码:

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1e5+5;

int n;
int a[N];

int main() {
    cin >> n;
    for (int i = 0; i < n; ++i) cin >> a[i];

    sort(a, a + n);
    
    int res = 0;
    for (int i = 0; i < n; ++i) res += abs(a[i] - a[n / 2]);
    
    cout << res << endl;
    return 0;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ypuyu

如果帮助到你,可以请作者喝水~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值