AtCoder Beginner Contest 188 F - +1-1x2 思维题

时间:2024-01-27 22:02:14

题目描述

给你两个数 \(x\),\(y\)

可以对 \(x\) 进行 \(+1,-1\)\(\times 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",n-m);
		return 0;
	}
	printf("%lld\n",dfs(m));
	return 0;
}