我是靠谱客的博主 优雅彩虹,这篇文章主要介绍6.2 贪心算法 | 排序不等式、绝对值不等式、推公式6.2 贪心算法 | 排序不等式、绝对值不等式、推公式,现在分享给大家,希望可以做个参考。

6.2 贪心算法 | 排序不等式、绝对值不等式、推公式

这是我的一个算法网课学习记录,道阻且长,好好努力

排序不等式

排队打水

例题: AcWing 913.排队打水

题目描述:

n 个人排队到 1 个水龙头处打水,第 i 个人装满水桶所需的时间是 ti,请问如何安排他们的打水顺序才能使所有人的等待时间之和最小?

输入格式

第一行包含整数 n。第二行包含 n 个整数,其中第 i 个整数表示第 i 个人装满水桶所花费的时间 ti

输出格式

输出一个整数,表示最小的等待时间之和。

数据范围

1≤ n ≤105,
1≤ ti ≤104

输入样例:

7
3 6 1 4 2 5 7

输出样例:

56

贪心思路:

按照从小到大的顺序排队,即有排队总时间最小。

证明思路:

调整法。

假设最优解并不是从小到大排序的,必然存在相邻两个数有ti > ti+1 ,容易知道对于其他两个人时间是没有变化的。

考虑第i和第i+1个同学的时间。

调整前:ti * (n-i) + ti+1 * (n-i-1) 调整后:ti+1 * (n-i) + ti * (n-i-1) 两者相减可得ti - ti+1 > 0,可以得到调整后是更小的总时间。经过多次调整后,可以获得从小到大排序的解,也就是最优解。

Ans

#include <iostream>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 100010;

int n; // 打水人数
int t[N]; // 每个人所需打水时间

int main()
{
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ ) scanf("%d", &t[i]);

    sort(t, t + n); // 从小到大排序

    LL res = 0;
    for (int i = 0; i < n; i ++ ) res += t[i] * (n - i - 1); // 累加

    printf("%lldn", res);

    return 0;
}

绝对值不等式

货仓选址

例题: AcWing 913.排队打水

在一条数轴上有 N 家商店,它们的坐标分别为 A1∼AN

现在需要在数轴上建立一家货仓,每天清晨,从货仓到每家商店都要运送商品。

为了提高效率,求把货仓建在何处,可以使得货仓到每家商店的距离之和最小

输入格式:

第一行输入整数 N。

第二行 N个整数 A1∼AN。

输出格式:

输出一个整数,表示距离之和的最小值。

数据范围

1≤N≤100000,
0≤Ai≤40000

输入样例:

4
6 2 9 1

输出样例:

12

在这里插入图片描述

贪心思路:

(思路其实包含证明了。)

核心是中位数。

在一个数轴上|a-x|+|b-x|>=|a-b|
xa,b正中间,取等号,即a,b到x距离之和最小

这个题是求多个点到一个点距离之和的最小值
可以俩俩配对,即a0an-1配对,a2an-2配对,以此类推

xn个数的中位数的时候距离和取最小

考虑最后中间单个还是双个,即n奇偶问题
n为奇数时,最后只剩下一个,中位数取这个数就ok,如果n为偶数个,中位数位于两个数之间。

Ans

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100010;

int n;
int a[N];

int main()
{
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);

    sort(a, a + n);

    int res = 0;
    for (int i = 0; i < n; i ++ ) res += abs(a[i] - a[n / 2]);

    printf("%dn", res);

    return 0;
}

推公式

很多贪心问题都是先把公式推出来,然后从公式的角度运用不等式的原理,可以得到我们最终的题解。

可以有意识的去看一些数学的证明。

耍杂技的牛

例题: AcWing 125.耍杂技的牛

农民约翰的 N 头奶牛(编号为 1...N)计划逃跑并加入马戏团,为此它们决定练习表演杂技。

奶牛们不是非常有创意,只提出了一个杂技表演:叠罗汉,表演时,奶牛们站在彼此的身上,形成一个高高的垂直堆叠。

奶牛们正在试图找到自己在这个堆叠中应该所处的位置顺序。这 N 头奶牛中的每一头都有着自己的重量 Wi 以及自己的强壮程度 Si

一头牛支撑不住的可能性取决于它头上所有牛的总重量(不包括它自己)减去它的身体强壮程度的值,现在称该数值为风险值,风险值越大,这只牛撑不住的可能性越高。

您的任务是确定奶牛的排序,使得所有奶牛的风险值中的最大值尽可能的小。

输入样例:

3
10 3
2 5
3 3

输出样例:

2

贪心思路:

按照wi + si从小到大的顺序排,最大的危险系数一定是最小的。

证明思路:

已知,对于 ∀ i ∈ [ 1 , n ] , i ∈ Z + forall i in [1,n] , i in Z^+ i[1,n],iZ+,有 w i > 0 , s i > 0 w_i>0,s_i>0 wi>0,si>0

考虑排序前和排序后的情况,对于第i头牛与第i+1头牛(其他牛的情况是不会发生改变的)

i头牛的危险系数i+1头牛的危险系数
交换前 ∑ j = 1 i − 1 w j − s i sum_{j=1}^{i-1}w_j-s_i j=1i1wjsi ∑ j = 1 i − 1 w j + w i − s i + 1 sum_{j=1}^{i-1}w_j+w_i-s_{i+1} j=1i1wj+wisi+1
交换后 ∑ j = 1 i − 1 w j + w i + 1 − s i sum_{j=1}^{i-1}w_j+w_{i+1}-s_{i} j=1i1wj+wi+1si ∑ j = 1 i − 1 w j − s i + 1 sum_{j=1}^{i-1}w_j-s_{i+1} j=1i1wjsi+1

观察可以发现所有的式子都共有一个部分 ∑ j = 1 i − 1 w j sum_{j= 1}^{i-1}w_j j=1i1wj,对其进行简化

i头牛的危险系数i+1头牛的危险系数
交换前 − s i -s_i si w i − s i + 1 w_i-s_{i+1} wisi+1
交换后 w i + 1 − s i w_{i+1}-s_{i} wi+1si − s i + 1 -s_{i+1} si+1

对于每头牛交换前后情况进行对比

对于第i头牛, − s i < w i + 1 − s i    ⟺    0 < w i + 1 - s_i < w_{i+1} - s_i iff 0 < w_{i + 1} si<wi+1si0<wi+1 ,交换后的危险系数更大。

对于第i+1头牛, − s i + 1 < w i − s i + 1    ⟺    0 < w i - s_{i+1} < w_{i} - s_{i+1} iff 0 < w_{i} si+1<wisi+10<wi,交换前的危险系数更大。

题目要求的是最大值当中风险系数最小,可以知道有两种情况,最大危险系数应关注 w i − s i + 1 w_i-s_{i+1} wisi+1 w i + 1 − s i w_{i+1}-s_{i} wi+1si

分类讨论之:

w i − s i + 1 ≥ w i + 1 − s i w_i-s_{i+1} ge w_{i+1}-s_{i} wisi+1wi+1si 时,即 w i + s i ≥ w i + 1 + s i + 1 w_i+s_i ge w_{i+1}+s_{i+1} wi+siwi+1+si+1 时,交换后最大危险系数的最小值更小,为更优解。

w i − s i + 1 < w i + 1 − s i w_i-s_{i+1} lt w_{i+1}-s_{i} wisi+1<wi+1si 时,即 w i + s i < w i + 1 + s i + 1 w_i+s_i lt w_{i+1}+s_{i+1} wi+si<wi+1+si+1 时,交换前最大危险系数的最小值更小,为更优解。

因此在算法中对于每头牛所对应的 s i + w i s_i+w_i si+wi进行排序,然后按照题意进行计算,最终就可以得到最优解。

Ans

#include <iostream>
#include <algorithm>

using namespace std;

typedef pair<int, int> PII;

const int N = 50010;

int n;
PII cow[N];

int main()
{
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ )
    {
        int w, s;
        scanf("%d%d", &w, &s);
        cow[i] = {w + s, w};
    }

    sort(cow, cow + n);

    int res = -2e9, sum = 0;
    for (int i = 0; i < n; i ++ )
    {
        int w = cow[i].second, s = cow[i].first - w;
        res = max(res, sum - s);
        sum += w;
    }

    printf("%dn", res);
    return 0;
}

最后

以上就是优雅彩虹最近收集整理的关于6.2 贪心算法 | 排序不等式、绝对值不等式、推公式6.2 贪心算法 | 排序不等式、绝对值不等式、推公式的全部内容,更多相关6.2内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(122)

评论列表共有 0 条评论

立即
投稿
返回
顶部