XTU 1192 Number(位运算)

http://202.197.224.59/OnlineJudge2/index.php/Problem/read/id/1192


Number

Accepted : 10   Submit : 140
Time Limit : 5000 MS   Memory Limit : 65536 KB

题目描述

你可以对数进行3种操作,+1,-1,*2,最开始的数是0,问最快多少次操作可以得到n(0≤n≤1018)。

输入

30000组样例,每行输入一个整数n。

输出

每行输出对应样例的结果。

样例输入

0
1
2
3
4


样例输出

0
1
2
3
3


Source

Eric


从0跑到n不好考虑,那就从n跑到0,如果n为偶数,那么n/2,如果是奇数那么n+1或n-1,很容易写个dfs,不过10^18显然要超时

把数转化成二进制考虑:

目标:把n消成0

如果最后一位是0,那么n>>1,如果最后一位是1,有两种方案把这位变成0,+1或-1,然后再>>1,所以单独消去1个0的开销为1,单独消去1个1的开销为2,显然我们希望每次操作后剩余的1尽可能少

末位为0,显然直接>>1,

末位为1的情况就有些复杂了:

考察10001:显然我们先-1变为10000,然后不断>>1变为1,然后-1到达0;如果+1,变为10010,1的个数不变,显然不是最优的方法,所以末尾只有1个1时,-1是最优的。

考察10011:先-1变为10010,>>1变为1001,-1变为1000,>>1变为100;

如果+1变为10100,>>1变为1010,>>1变为101,-1变为100;

对于两种方案,开销都为4,所以末尾如果有2个1,+1,-1都是一样的。

考察末尾有k个1(k>2),我们发现+1是更优的操作。

但是对于1111011这种情况,我们发现+1比-1的开销更少,可以多写几个数模拟验证一下,所以末尾有2个1,+1其实比-1更优


所以我们对数进行如下操作:

n为偶数,则n>>1;

n为奇数且末位只有1个1,n-1;

n为奇数且末位有多个连续的1,n+1。


#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define sqr(x) ((x)*(x))
#define LL unsigned long long
#define INF 0x3f3f3f3f
#define PI 3.14159265358979
#define eps 1e-10
#define mm

using namespace std;

LL n,N;
int k;
LL bit[65];

void init()
{
    bit[1]=1;
    for (int i=2;i<65;i++)
    {
        bit[i]=bit[i-1]<<1;
    }
}

LL dfs(LL n,LL t)
{
    if (n==0)
        return t;

    if (n==1)
        return t+1;

    if (n==3)
        return t+3;

    if ((n&1)==1)
    {
        k=2;
        while ((n&bit[k])) k++;
        k--;

        if (k==1)
            return dfs(n>>1,t+2);
        else
            return dfs(n+1,t+1);
    }
    else
    {
        return dfs(n>>1,t+1);
    }
}

int main()
{
    init();

    for (int i=0;i<30000;i++)
    {
        scanf("%I64u",&n);
        N=n+2;
        printf("%I64u\n",dfs(n,0));
    }


	return 0;
}



你可能感兴趣的:(数学,位运算)