积木大赛(差分)
题意
有\(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;
}