题目描述
给你两个数 (x),(y)
可以对 (x) 进行 (+1,-1) 或 ( imes 2) 的操作
问最少操作多少次后变为 (y)
(x,y leq 10^{18})
分析
将问题转化为从 (y) 变为 (x),可以进行 (+1,-1) 或 (div 2) 的操作
之所以这样做,是因为转化题意之后操作更受约束
如果当前值为偶数,则只能除以 (2)
如果当前值为奇数,则只能执行加 (1) 或减 (1) ,再除以 (2)
因为如果要加更多的数,完全可以在除以 (2) 之后再加
相比之下,你可以随时加 (1) 或乘 (2),所以有更多的分支
如果你把它想象成一个根树遍历,那就好像如何重复地找到父对象要比探索所有子对象简单得多
这样做复杂度看起来还是 (2^{logy}=y)
实际上,若 (y) 为偶数,只会有 (y/2) 一个分支
若 (y) 为奇数,会有 ((y+1)/2) 和 ((y-1)/2) 两个分支
而且这两个数是相邻的,设为 (a,a+1)
如果 (a) 为奇数,下一层递归会有 ((a+1)/2,(a-1)/2,(a+1)/2) 三个分支
如果我们采用记忆化搜索的方式,只会递归到两个分支
同样若 (a) 为偶数,下一层递归会有 (a/2,a/2,(a+2)/2) 三个分支
记忆化后也变为了两个分支
因为每一次都会除以 (2),所以最多递归 (log) 层
而每一层只有两个不同的数,所以最终的状态是 (log) 级别的
代码
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#include<map>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
long long n,m;
std::map<long long,long long>mp;
long long dfs(rg long long now){
if(mp.find(now)!=mp.end()) return mp[now];
if(now<=n) return n-now;
rg long long nans=now-n;
if(now&1){
nans=std::min(nans,std::min(dfs((now-1)/2),dfs((now+1)/2))+2);
} else {
nans=std::min(nans,dfs(now/2)+1);
}
return mp[now]=nans;
}
int main(){
scanf("%lld%lld",&n,&m);
if(n>=m){
printf("%lld
",n-m);
return 0;
}
printf("%lld
",dfs(m));
return 0;
}