题目链接
输入样例:
5
2 3 4 1 2
输出样例:
5
分析: y总写的太好,直接转发了
算法
(差分,贪心)
O
(
n
)
O(n)
O(n)
我们逆向思考:假设给定了每块积木的高度,每次可以将某一段区间中的所有高度减一,问最少操作多少次可以将所有高度变成0。
原序列是: h 1 , h 2 , h 3 , … , h n , 其 中 h i ≥ 1 h_1,h_2,h_3,…,h_n, 其中 h_i≥1 h1,h2,h3,…,hn,其中hi≥1。
构造差分序列:
b
1
=
h
1
b_1=h_1
b1=h1
b
2
=
h
2
−
h
1
b_2=h_2−h_1
b2=h2−h1
b
3
=
h
3
−
h
2
b_3=h_3−h_2
b3=h3−h2
…
…
…
b
n
=
h
n
−
h
n
−
1
b_n=h_n−h_{n−1}
bn=hn−hn−1
b
n
+
1
=
−
h
n
b_{n+1}=−h_n
bn+1=−hn
则将
h
L
,
h
L
+
1
,
…
,
h
R
h_L,h_{L+1},…,h_R
hL,hL+1,…,hR中的每个数减1的操作,等价于将 b_L 减1, 将
b
R
+
1
b_{R+1}
bR+1 加1。
因此问题变成每次从 b 1 , b 2 , … , b n b_1,b_2,…,b_n b1,b2,…,bn 中挑两个数,前一个减1,后一个加1,问最少操作多少次可以将所有数变成0。
首先,对于每个正数 b i > 0 b_i>0 bi>0,最少需要操作 b i b_i bi 次,因此总操作次数一定不少于差分数组中所有正数之和 B。
然后我们构造一种操作方式,使得恰好可以通过 B 次操作,将所有数变成0。 那么就可以说明最少操作次数一定是所有正数之和。
操作方式如下:
从后往前操作,如果当前的
b
i
>
0
bi>0
bi>0,则将其减1,并将其后的某个负数加1。
由于
∑
b
i
=
0
∑b_i=0
∑bi=0,因此从数量上看,正数和负数是一一对应的。
然后我们考虑在操作过程中,对于每个正数 bibi,其后是否一定存在负数与之对应。由于差分数组的所有后缀
∑
i
=
k
n
=
1
b
i
=
−
b
k
<
=
0
\sum_{i=k}^{n=1}b_i=-b_k<=0
∑i=kn=1bi=−bk<=0,因此在所有后缀中,正数之和一定小于等于负数之和,所以负数总是可以被找到的。
因此,上面的操作方式是合法的。
时间复杂度
求差分序列扫描一遍数组即可,因此总时间复杂度是
O
(
n
)
O(n)
O(n)。
参考作者:yxc
链接:https://www.acwing.com/solution/content/3256/
代码如下
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N=1e5+10;
int n,q[N],b[N],res=0;
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>q[i];
for(int i=1;i<=n;i++ )
{
b[i]+=q[i];
b[i+1]-=q[i];
res+=max(0,b[i]);
}
cout<<res<<endl;
return 0;
}