积木大赛(差分)

题意

\(n\)个积木,给定它们的高度\(h_i\),每次可以将某一段区间中的所有高度减一,问最少操作多少次可以将所有高度变成\(0\)

数据范围

\(1 \leq n \leq 10^5\)
\(0 \leq h_i \leq 10000\)

思路

构造差分序列:

\[b_1 = a_1 \\ b_2 = a_2 - a_1 \\ b_3 = a_3 - a_2 \\ \dots \\ b_n = a_n - a_{n - 1} \\ b_{n + 1} = -a_n \]

若原序列全都变为\(0\),那么差分序列也全变成了\(0\)。因此原问题等价于每次从 \(b_1,b_2,\dots ,b_{n+1}\)中挑两个数,前一个减\(1\),后一个加\(1\),多少次操作可以将差分序列全变成\(0\)

对于差分序列中每个正数 \(b_i\),要将其减为\(0\),最少需要操作 \(b_i\)次,因此总操作次数一定不少于差分数组中所有正数之和 \(B\)

然后我们能够构造一种操作方式,使得恰好可以通过 \(B\) 次操作,将所有数变成\(0\)。 那么就可以说明最少操作次数一定是所有正数之和。

操作方式为:从后往前遍历,找到第一个整数,对其进行减\(1\)操作,其后必存在一个负数,对其进行加\(1\)操作。

对于每个正数\(b_i\),其后必存在一个负数,原因是对\(b_i\)及其往后的数字求和得到的是\(-a_k < 0\),因此必存在负数。

因此,最终答案就是差分数组中所有正数之和。

代码

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int N = 100010;

int n;
int a[N];

int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
    int ans = 0;
    for(int i = 1; i <= n; i ++)
        if(a[i] > a[i - 1])
            ans += (a[i] - a[i - 1]);
    printf("%d\n", ans);
    return 0;
}
posted @ 2021-03-10 18:53  pbc的成长之路  阅读(60)  评论(0编辑  收藏  举报