问题描述
看完电影后,乐乐回家玩起了积木。 他已经搭好了 n 堆积木,他想通过调整积木,使得其中有连续 W 堆积木具有相同的高度,同时他希望高度恰好为 H 。 乐乐的积木都这了,也就是说不能添加新的积木,只能移动现有的积木。 他可以把一个积木从一堆移动到另一堆或者新的一堆,但是不能移动到两堆之间。比如,一次移动之后,"3 2 3" 可以变成 "2 2 4" 或者 "3 2 2 1",但是不能变成"3 1 1 3". 请你帮他算算,需要移动的最少积木数。
输入描述
有多组测试数据,大约 100 组。 第一行三个整数, n,W,H,n 表示有多少堆积木。 第二行 n 个元素,表示这 n 座积木的高度。 所有数据的范围 [1,50000] ;
输出描述
输出最少需要移动的次数,如果无法完成输出-1。
输入样例
4 3 2 1 2 3 5 4 4 4 1 2 3 4
输出样例
1 -1
Hint
样例解释:把第3座积木上的一个积木移动到第1座上,前3座积木的高度就变成了2 2 2,就得到了一个3*2(积木必须是连续的W堆)。
分析:因为固定了区间的宽度为W,而木块可能出现的区间只有 -w ~ n+w,所以我们把整个1~n这个区间的左边和右边分别加上一个长度为w积木高度为0的区间。然后枚举每个区间需要移动的次数,只需n+w次即可找到最后的结果,而每个区间的移动次数 = 外+(总-外)/2。外就是这个区间和目标个数w*h差了多少个,总就是这个区间总共移动的次数,其中内部的移动算了两次,所以要除以2。总的减去要移到外部的或外部要进来的,就是内部的移动次数。
ps:g++超时,c++AC
AC代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstdlib>
using namespace std;
const int maxn = 50005;
int n,w,h;
int a[maxn*3];
long long res;
long long sum;
long long sum2;
long long sum3;
int main(){
//freopen("data.in","r",stdin);
//freopen("data.out","w",stdout);
while(scanf("%d%d%d",&n,&w,&h) == 3){
sum = 0;
sum2 = 0;
sum3 = 0;
for(int i = 1; i <= w; i++){
a[i] = 0 - h;
}
for(int i = w+1; i <= n+w; i++){
scanf("%d",&a[i]);
sum += a[i];
a[i] -= h;
}
for(int i = w+n+1; i <= 2*w+n; i++){
a[i] = 0 - h;
}
if(sum < 1LL*w*h){
printf("-1\n");
continue;
}
for(int i = 1; i <= w; i++){
sum2 += a[i];
sum3 += abs(a[i]);
}
res = abs(sum2) + (sum3 - abs(sum2))/2;
for(int i = w+1; i <= 2*w+n; i++){
sum2 += a[i];
sum2 -= a[i-w];
sum3 += abs(a[i]);
sum3 -= abs(a[i-w]);
res = min(res,abs(sum2) + (sum3 - abs(sum2))/2);
}
printf("%I64d\n",res);
//前缀和
}
return 0;
}