【HDOJ 5802】Windows 10(贪了个心)

【HDOJ 5802】Windows 10(贪了个心)

Windows 10

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

Problem Description
Long long ago, there was an old monk living on the top of a mountain. Recently, our old monk found the operating system of his computer was updating to windows 10 automatically and he even can’t just stop it !!
With a peaceful heart, the old monk gradually accepted this reality because his favorite comic LoveLive doesn’t depend on the OS. Today, like the past day, he opens bilibili and wants to watch it again. But he observes that the voice of his computer can be represented as dB and always be integer.
Because he is old, he always needs 1 second to press a button. He found that if he wants to take up the voice, he only can add 1 dB in each second by pressing the up button. But when he wants to take down the voice, he can press the down button, and if the last second he presses the down button and the voice decrease x dB, then in this second, it will decrease 2 * x dB. But if the last second he chooses to have a rest or press the up button, in this second he can only decrease the voice by 1 dB.
Now, he wonders the minimal seconds he should take to adjust the voice from p dB to q dB. Please be careful, because of some strange reasons, the voice of his computer can larger than any dB but can’t be less than 0 dB.

Input
First line contains a number T (1T300000),cases number.
Next T line,each line contains two numbers p and q (0p,q109)

Output
The minimal seconds he should take

Sample Input
2
1 5
7 3

Sample Output
4
4

Author
UESTC

Source
2016 Multi-University Training Contest 6

题目大意:
电视机当前音量为p,想要调到q
有三中操作:
1. 加音量:一次只能+1
2. 减音量:严格二倍的减,如 1248
3. 停顿:占用一次操作,停顿后下一次减音量会重新从 1 开始。
减音量与加音量之间的转换不需要停顿。

问从音量p变为q需要的最少操作次数。

立即可以想到,如果 p<=q ,答案即为 qp

当p > q时。
w = p-q为需要减去的音量。
首先考虑纯减不加
那么会变成
124812412

这种操作方式( 为停顿。)。
把每两次停顿间的操作当做一个块。
然后考虑添加加音量操作。从最低块开始。
以上为例,最低块+5后可以与它的上块合并。很容易证明,如果不合并,无论加多少音量都不如原状态更优。
这样可以把每个块表示为2ai,为每个块的最后一步减操作减去的音量。并且处理出的a数组一定是单调不递减的。
同时处理一个数组 toti 表示第i个块减去的音量。
这样当 i 块与i1块合并时,需要加的音量就是 ai1toti 。处理过程中要更新 i1 块的两个数组

然后再考虑加音量步骤的位置和停顿位置。
在处理出块的时候可以一并处理出来停顿的次数。
这样当压缩块的时候,可以优先把加音量操作塞到停顿处,也就是尽量减少操作。

然后xjb搞一下就出来结果了。

有个坑就是音量不能减为负,也就是说可能压缩块时一些加操作是没用的。因为减操作其实并没有减够,减为0就会cut,但是压缩的时候那部分溢出的减操作也会被无形的用加操作抵消。也就是出现了多余的加操作。

处理方法:暴力减到 <= q,然后加到p,会得到一个操作数。与压块的贪心操作数取最小即可

代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <vector>
#include <stack>
#include <map>
#include <algorithm>
#include <cmath>
#include <ctime>
#define LL long long
#define Pr pair<int,int>
#define fread(ch) freopen(ch,"r",stdin)
#define fwrite(ch) freopen(ch,"w",stdout)

using namespace std;
const int INF = 0x3f3f3f3f;
const double eps = 1e-8;

int a[66],b[66],c[66];
int tp;

int main()
{
    int t,n,m;
   //fread("");
   //fwrite("");

   int p,q;
    scanf("%d",&t);

    while(t--)
    {
        scanf("%d%d",&p,&q);
        int ans;

        if(q >= p) printf("%d\n",q-p);
        else
        {
            tp = 0;

            int tmp,k = 0;

            int x = 1;
            int cnt = 0;
            tmp = 0;

            while(tmp <= p-q)
            {
                tmp += x;
                x *= 2;
                cnt++;
            }

            //暴力减,触底反弹
            int mx = cnt;
            mx += min(tmp-(p-q),q);

            p -= q;



            ans = 0;

            //压缩成块
            while(p)
            {
                x = 1;
                //块的总减去音量数
                b[tp] = 0;
                //块中减操作次数
                c[tp] = 0;
                while(x <= p)
                {
                    p -= x;
                    b[tp] += x;
                    c[tp]++;
                    ans++;
                    x *= 2;
                }

                //块下界
                a[tp++] = x;
                //不是最后一个块,停顿+1
                if(p)
                {
                    ans++;
                    k++;
                }
            }

            //单纯的减操作,停顿存在k中
            tmp = ans-k;

            int ad = 0;
            for(int i = tp-1; i >= 1; --i)
            {
                //压缩i块与i-1块
                ad += a[i-1]-b[i];

                tmp -= c[i];
                tmp++;
                k--;

                b[i-1] += a[i-1];
                c[i-1]++;

                ans = min(ans,tmp+ad+max(0,k-ad));
            }
            printf("%d\n",min(mx,ans));
        }
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值