题目
Description
小 H 是一个建筑师,他接到了一个任务——按照计划图搭建一排楼房。计划图上从左到右
给出了 n 个非负整数,对于第 i 个数 h i ,它表示在 i 这个位置搭建出来的楼房的高度不能小于h i 。
小 H 搭建楼房的方式也很特别。在每一时刻,它总可以让相邻的两个楼房分别增高 1 个单
位和增高 2 个单位。具体地,对于任意的 i(1 ≤ i < n),每一时刻他可以有以下两种搭建的方法:
- 让 i 位置上的楼房的高度 +2,同时让 i + 1 位置上的楼房 +1。
- 让 i 位置上的楼房的高度 +1,同时让 i + 1 位置上的楼房 +2。
小 H 想知道最快需要多少时间,搭建出来的这一排楼房才能满足计划图的要求?
Input
第一行一个整数 n。
第二行 n 个整数,分别为 h 1 ,h 2 ,···,h n 。
Output
一行一个答案,表示最快需要多少时间。
Sample Input
【样例 1 输入】
4
2 1 1 2
【样例 2 输入】
13
12 13 9 8 3 21 7 10 1 1 8 1 1
Sample Output
【样例 1 输出】
2
【样例 2 输出】
35
Data Constraint
Hint
【样例 1 解释】
最开始四个楼房的高度为 0 0 0 0,小 H 最少需要如下两次搭建:
- 给第 1、2 个位置分别 +2 和 +1,高度变为 2 1 0 0。
- 给第 3、4 个位置分别 +1 和 +2,高度变为 2 1 1 2。
思路
先考虑一个显然错误的贪心。遍历一遍,当 ai<hi 时,就令 ai+=2,ai+1++。否则 i++。这样只能保证前面的 i 的操作次数最少,不能保证总操作次数最少。
考虑三种反悔操作:
若之前执行了 ai+=2,ai+1++。考虑撤销这个操作,改成 ai++,ai+1+=2,然后再加一步 ai++,ai+1+=2。这样使得 ai 不变,并且花 1 的代价让 ai+1+=3。
若之前执行了 ai+=3。考虑撤销这个操作,改成 ai++,ai+1+=2,然后再加一步 ai+=2,ai+1++。这样使得 ai 不变,并且花 1 的代价让 ai+1+=3。
若之前执行了 ai+=3。考虑撤销这个操作,但是加上 3 次的 ai++,ai+1+=2。这样使得 ai 不变,并且花 2 的代价让 ai+1+=6。
我们的贪心还是保证靠前的 ai 的操作次数尽量小。因此记一下之前用了几个 ai−1+=3,用了几个 ai−1+=2,ai++,即可知道能用多少次 ai+=3。如果次数都用完了还是 ai<hi,设此时 hi−ai=k,那么我们用 ⌊k2⌋ 次 ai+=2,ai+1++,用 k%2 次 ai++,ai+1+=2 即可。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e6+7;
int t1,t2,t3,h[N],a[N],n;
ll ans;
int main()
{
freopen("building.in","r",stdin); freopen("building.out","w",stdout);
scanf("%d",&n);
for(int i=1; i<=n; i++) scanf("%d",&h[i]);
for(int i=1; i<=n; i++)
{
h[i]=max(0,h[i]);
t1=min(h[i]/3,a[i]);
h[i]-=t1*3;
t2=h[i]/2;
h[i]-=t2*2;
t3=h[i];
h[i]-=t3;
ans+=t1+t2+t3;
a[i+1]+=t1*2+t2;
h[i+1]-=t2+t3*2;
}
printf("%lld",ans);
}