春春幼儿园举办了一年一度的“积木大赛”。

今年比赛的内容是搭建一座宽度为n的大厦,大厦可以看成由n块宽度为1的积木组成,第i块积木的最终高度需要是hiℎi。

在搭建开始之前,没有任何积木(可以看成块高度为 0 的积木)。

接下来每次操作,小朋友们可以选择一段连续区间[L,R],然后将第L块到第R块之间(含第 L 块和第 R 块)所有积木的高度分别增加1。

小M是个聪明的小朋友,她很快想出了建造大厦的最佳策略,使得建造所需的操作次数最少。

但她不是一个勤于动手的孩子,所以想请你帮忙实现这个策略,并求出最少的操作次数。

输入格式

输入包含两行,第一行包含一个整数n,表示大厦的宽度。 

第二行包含n个整数,第i个整数为hiℎi。

输出格式

仅一行,即建造所需的最少操作数。

数据范围

1n1051≤n≤105,
0hi100000≤hi≤10000

输入样例:

5
2 3 4 1 2

输出样例:

5

算法

(差分,贪心) O(n)
我们逆向思考:假设给定了每块积木的高度,每次可以将某一段区间中的所有高度减一,问最少操作多少次可以将所有高度变成0。

原序列是: h1,h2,h3,…,hn 其中 hi≥1。

构造差分序列:

b1=h1

b2=h2−h1

b3=h3−h2

bn=hn−hn−1

bn+1=−hn

则将 hL,hL+1,…,hRhL,hL+1,…,hR 中的每个数减1的操作,等价于将 bL 减1, 将 bR+1 加1。关于差分算法可以参考 AcWing 797.。

因此问题变成每次从 b1,b2,…,bn+1中挑两个数,前一个减1,后一个加1,问最少操作多少次可以将所有数变成0。

首先,对于每个正数 bi>0,最少需要操作 bi 次,因此总操作次数一定不少于差分数组中所有正数之和 B。

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

操作方式如下:

从后往前操作,如果当前的 bi>0,则将其减1,并将其后的某个负数加1。
由于 ∑bi=0,因此从数量上看,正数和负数是一一对应的。
然后我们考虑在操作过程中,对于每个正数 bi,其后是否一定存在负数与之对应。由于差分数组的所有后缀 ∑n+1i=kbi=−hk≤0,因此在所有后缀中,正数之和一定小于等于负数之和,所以负数总是可以被找到的。

因此,上面的操作方式是合法的。

时间复杂度

求差分序列扫描一遍数组即可,因此总时间复杂度是 O(n)O(n)。

作者:yxc
链接:https://www.acwing.com/solution/content/3256/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 
 
 
/*
因此问题变成每次从差分数组: b1,b2,…,bn+1b1,b2,…,bn+1 中挑两个数,
前一个减1,后一个加1,问最少操作多少次可以将所有数变成0。
首先,对于每个正数 bi>0,最少需要操作 bi 次,因此总操作次数一定不少于
差分数组中所有正数之和 B。然后我们构造一种操作方式,使得恰好可以通过 B 次操作,
将所有数变成0。 那么就可以说明最少操作次数一定是所有正数之和。
由于差分数组的所有后缀 ∑i=kn+1bi=-hk≤0,因此在所有后缀中,
正数之和一定小于等于负数之和,所以负数总是可以被找到的。
因此,上面的操作方式是合法的。
求差分序列扫描一遍数组即可,因此总时间复杂度是 O(n)。
*/
#include<iostream>
using namespace std;
int n,ans=0;
int a[100100];
int main(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++) //差分数组的灵活应用 
		if(a[i]-a[i-1]>0) ans+=a[i]-a[i-1];
	cout<<ans<<endl;
	return 0;
}