Building Blocks
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 2210 Accepted Submission(s): 510
LeLe has already built n piles. He wants to move some blocks to make W consecutive piles with exactly the same height H .
LeLe already put all of his blocks in these piles, which means he can not add any blocks into them. Besides, he can move a block from one pile to another or a new one,but not the position betweens two piles already exists.For instance,after one move,"3 2 3" can become "2 2 4" or "3 2 2 1",but not "3 1 1 3".
You are request to calculate the minimum blocks should LeLe move.
The first line of input contains three integers n,W,H(1≤n,W,H≤50000) . n indicate n piles blocks.
For the next line ,there are n integers A1,A2,A3,……,An indicate the height of each piles. (1≤Ai≤50000)
The height of a block is 1.
If there is no solution, output "-1" (without quotes).
4 3 2 1 2 3 5 4 4 4 1 2 3 4
1 -1HintIn first case, LeLe move one block from third pile to first pile.
堆的移动规则为每次可以从一个堆顶部拿一个东西放到另一个堆顶部或者放到最左或最右形成一个新的高度为1的堆
现在求最少移动次数
思路:死活想不出来,只能去看题解,甚是巧妙啊....
想一下就可以发现无法达成只有一种条件就是所有堆的总和sum<w*h
因为如果sum>=w*h,那么无论是如何排列的,我们总能通过移动达成条件。 并且最多移动w*h次(可以想象一下每次都把东西放到最边上,这样w*h次必定可以达成条件)
那么此时我们考虑任意一个长度为w的区间最少需要移动多少次。
首先分为三种情况
第一种:当前区间的东西总和sum<w*h,那么为了凑到w*h,我们要从区间外的堆里拿w*h-sum个东西进来,并且把这些东西都放在高度不足h的堆上。
放完之后,剩下的只有两种情况了,第一种,已经完全满足条件,w个堆都是h。第二种,还有部分堆高度<h,部分堆高度>h,那么因为总和是w*h,所以将<h和>h的堆的欠缺数量一定相等。 所以这种情况需要的最少操作次数是w*h-sum+big_num(big_num表示该区间所有大于h的堆都移除一些东西使得满足都为h的操作数)
第二种:当前区间的东西总和sum>w*h,跟第一种类似。 所以这种情况需要的最少操作次数是sum-w*h+low_num(low_num表示该区间所有小于h的堆都增添一些东西使得满足都为h所需的操作数)
第三种,当前区间的东西总和sum=w*h,那么此时不需要从区间外取东西了。所需最少操作数是big_num/low_num
到这样这道题就完成了一大半了。注意到还有一种情况是比如
3 3 3
9 100 100
答案是6,即3 3 3 100 100
也就是对于最左边和最右边,有可能结果是新增添的堆来的。所以我们最开始的时候需要在最左边和最右边插入w个0,表示左右各有高度为0的w个堆,然后情况就跟上面一样了。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
#define N 500500
long long INF=2600000000;
long long a[N];
int main()
{
int n;
long long w,h;
while(~scanf("%d %lld %lld",&n,&w,&h))
{
long long sum=0;
memset(a,0,sizeof(a));
for(int i=w+1; i<=n+w; i++)
{
scanf("%lld",&a[i]);
sum+=a[i];
}
if(sum<w*h) printf("-1\n");
else
{
sum=0;
long long big_sum=0,low_sum=w*h;
long long ans=w*h;
for(int i=w+1;i<=n+w+w;i++)
{
sum-=a[i-w];
sum+=a[i];
if(a[i-w]>h) big_sum-=(a[i-w]-h);
if(a[i-w]<h) low_sum-=(h-a[i-w]);
if(a[i]>h) big_sum+=(a[i]-h);
if(a[i]<h) low_sum+=(h-a[i]);
if(sum<w*h)
ans=min(ans,w*h-sum+big_sum);
else if(sum>w*h)
ans=min(ans,sum-w*h+low_sum);
else ans=min(ans,low_sum);
}
printf("%lld\n",ans);
}
}
return 0;
}