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 SourceEric |
从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<cstdio>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<ctime>
#include<cctype>
#include<cmath>
#include<string>
#include<cstring>
#include<stack>
#include<queue>
#include<vector>
#include<map>
#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;
}