hdu 5191 Building Blocks(模拟,思路)

这是一篇关于HDU 5191 Building Blocks问题的解析,文章介绍了如何通过模拟移动堆来达到最少操作次数的策略。核心在于判断区间元素总和与目标值的关系,分为三种情况讨论:总和小于、大于和等于目标值。最后,通过添加虚拟的0堆以处理特殊情况。

Building Blocks

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 2210    Accepted Submission(s): 510


Problem Description
After enjoying the movie,LeLe went home alone. LeLe decided to build blocks.
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.
 

Input
There are multiple test cases, about 100 cases.

The first line of input contains three integers n,W,H(1n,W,H50000).n indicate n piles blocks.

For the next line ,there are n integers A1,A2,A3,,An indicate the height of each piles. (1Ai50000)

The height of a block is 1.
 

Output
Output the minimum number of blocks should LeLe move.

If there is no solution, output "-1" (without quotes).
 

Sample Input
4 3 2 1 2 3 5 4 4 4 1 2 3 4
 

Sample Output
1 -1
Hint
In first case, LeLe move one block from third pile to first pile.
题意:有n个堆,每个堆有a[i]个东西,现在要求移动堆上的东西使得存在连续的w个高度为h的堆

堆的移动规则为每次可以从一个堆顶部拿一个东西放到另一个堆顶部或者放到最左或最右形成一个新的高度为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;
}


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值