题目
分析
图中每列代表一堆石子,高度代表石子数量,k=3
如何遍历?
- 最终高度是多少?有可能很高,因此,即便知道一个加石子的方案,一次一次地加,也是复杂度太高。
- 往k个石子堆添加一块石子时,称最左边的堆所在的位置为添加起点。显然,按添加起点可将所有添加石子的动作分为n-k+1类,则遍历这些分类可行
- 从左向右遍历分类,则贪心成立。贪心策略: 先将最左边的一次性加到目标高度,然后是左边的第二个位置,依次类推。
- 那么目标高度是多少?
- 初始的高度目标可以设为初始的最大值,在从左往右的添加过程中,若出现了更大的高度,则更新目标高度
- 新的高度出现后,应立即使得左侧已达到原有目标高度的达到新高度,若成功则继续,否则失败结束。
- 添加石子是区间操作,因此采用差分数组维护数据。
代码
#include<bits/stdc++.h>
using namespace std;
#define MAXN 100010
long long ans, mx, dh[MAXN], delt;
int T, a[MAXN], n, k;
int main(){
scanf("%d", &T);
while(T--){
scanf("%d %d", &n, &k), mx = 0, ans = 0;
for(int i = 1; i <= n; i++)
scanf("%d", a+i), mx = max(mx, (long long)a[i]);
memset(dh, 0, sizeof dh);
for(int i = 1; i <= n; i++){
dh[i] += dh[i-1];
if(dh[i] + a[i] > mx) {
if((i-1)%k != 0) {
ans = -1;
break;
}
ans += (i-1) / k *(dh[i]+a[i] - mx);
mx = dh[i] + a[i];
}
else if(dh[i] + a[i] < mx){
if(i+k > n+1){
ans = -1;
break;
}
delt = mx - dh[i] - a[i];
dh[i] += delt, dh[i+k] -= delt;
ans += delt;
}
}
printf("%lld\n", ans);
}
return 0;
}