题目相关
题目链接
AtCoder Beginner Contest 188 F 题,https://atcoder.jp/contests/abc188/tasks/abc188_f。
Problem Statement
Takahashi has written an integer X X X on a blackboard. He can do the following three kinds of operations any number of times in any order:
- increase the value written on the blackboard by 1 1 1;
- decrease the value written on the blackboard by 1 1 1;
- multiply the value written on the blackboard by 2 2 2.
Find the minimum number of operations required to have Y Y Y written on the blackboard.
Input
Input is given from Standard Input in the following format:
X Y
Output
Print the answer.
Sample 1
Sample Input 1
3 9
Sample Output 1
3
Explaination
Initially, 3 3 3 is written on the blackboard. The following three operations can make it 9 9 9:
increase the value by 1 1 1, which results in 4 4 4;
multiply the value by 2 2 2, which results in 8 8 8;
increase the value by 1 1 1, which results in 9 9 9.
Sample 2
Sample Input 2
7 11
Sample Output 2
3
Explaination
The following procedure can make the value on the blackboard 11 11 11:
decrease the value by 1 1 1, which results in 4 4 4;
multiply the value by 2 2 2, which results in 12 12 12;
decrease the value by 1 1 1, which results in 11 11 11.
Sample 3
Sample Input 3
1000000000000000000 1000000000000000000
Sample Output 2
0
Explaination
If the value initially written on the blackboard equals Y Y Y, print 0 0 0.
Constraints
- 1 ≤ X ≤ 1 0 18 1≤X≤10^{18} 1≤X≤1018
- 1 ≤ Y ≤ 1 0 18 1≤Y≤10^{18} 1≤Y≤1018
- X X X and Y Y Y are integers.
题解报告
题目翻译
高桥在黑板上写了一个 X X X,他可以对这个数字进行任意次如下操作:
- 将黑板上的数字加 1 1 1;
- 将黑板上的数字减 1 1 1;
- 将黑板上的数字乘以 2 2 2。
请找出变成 Y Y Y 的最小次数。
题目分析
本题的关键字是找到最小次数,因此本题可以使用 BFS 来解决。也就是套用 BFS 框架。
可见性控制
本题是从 X X X 变成 Y Y Y,而且变化的方法是: + 1 , − 1 , ∗ 2 +1, -1, *2 +1,−1,∗2,所以我们只需要考虑从 1 1 1 到 2 ∗ Y 2*Y 2∗Y 的范围即可。但是由于 Y Y Y 的最大值是 1 0 18 10^{18} 1018,直接定义这么大的数组肯定程序就爆了。
可以考虑使用 STL 的 map 来,即读入 Y Y Y 以后,再定义一个 map。
其他方面我们套用 BFS 的模板就可以。
BFS TLE 代码
//https://atcoder.jp/contests/abc188/tasks/abc188_f
//F - +1-1x2
#include <bits/stdc++.h>
using namespace std;
//如果提交到OJ,不要定义 __LOCAL
//#define __LOCAL
typedef long long ll;
map<ll, bool> vis;
typedef struct _DATA{
ll val;
ll dis;
} DATA;
//从 x 到 y
ll bfs(ll x, ll y) {
queue<DATA> q;
q.push({
x, 0});
vis[x] = true;
while (false == q.empty()) {
DATA cur = q.front();
q.pop();
//终点
if (cur.val == y) {
return cur.dis;
}
DATA now;
now.dis = cur.dis+1;
//increase 1
now.val = cur.val+1;
if (false==vis[now.val]) {
vis[now.val] = true;
q.push(now);
}
//decrease 1
now.val = cur.val-1;
if (now.val>=1 && false==vis[now.val]) {
vis[now.val] = true;
q.push(now);
}
//multiply 2
now.val = cur.val*2;
if (false==vis[now.val]) {
vis[now.val] = true;
q.push(now);
}
}
return 0;
}
int main() {
#ifndef __LOCAL
//这部分代码需要提交到OJ,本地调试不使用
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
#endif
ll x, y;
cin>>x>>y;
if (x==y) {
cout<<"0\n";
return 0;
} else if (x>y) {
cout<<x-y<<"\n";
return 0;
}
cout<<bfs(x, y)<<"\n";
#ifdef __LOCAL
//这部分代码不需要提交到OJ,本地调试使用
system("pause");
#endif
return 0;
}
剪枝
很可惜,本题的难点还是在于数据太大,最大的可能是 X = 1 , Y = 1 0 18 X=1, Y=10^{18} X=1,Y=1018,这样我们 BFS 次数太多,导致 TLE。因此要考虑剪枝操作。下面我们来分类讨论一下:
X < Y X<Y X<Y 的时候
这种情况下,说明我们每次操作只能是 − 1 -1 −1,其他两个操作都不可能,因此其他两个操作数据都是变大。这样,最小的可能就是 a n s = Y − X ans = Y-X ans=Y−X。
Y = X Y=X Y=X 的时候
这种情况下,我们需要任何操作已经完成,因此 a n s = 0 ans=0 ans=0。
X > Y X>Y X>Y
本题移动方案有三种: + 1 +1 +1、 − 1 -1 −1、 ∗ 2 *2 ∗2。由于要求最小步数,因此可以考虑贪心的思路,尽量先操作 ∗ 2 *2 ∗2。这样,当前位置为偶数,我们直接除以 2 2 2;当前位置为奇数,我们可以有两种操作,即加 1 1 1 除 2 2 2 或者减 1 1 1 除 2 2 2。这样我们可以大大缩短 BFS 时间。
但是这样优化后,距离判断就要除问题,我们需要用其他变量来表示实际的 ans。我们将新的位置记为 N e w Y New_Y NewY,这样 a n s = d i s + ∣ N e w Y − X ∣ ans=dis+\lvert New_Y-X\rvert ans=dis+∣NewY−X∣。这样我们只需要取 ans 的最小值即可。
剪枝优化代码
//https://atcoder.jp/contests/abc188/tasks/abc188_f
//F - +1-1x2
#include <bits/stdc++.h>
using namespace std;
//如果提交到OJ,不要定义 __LOCAL
//#define __LOCAL
typedef long long ll;
typedef struct _DATA{
ll val;
ll dis;
} DATA;
//从 x 到 y
void bfs(ll x, ll y) {
ll ans = 1e18;
map<ll, ll> dis;//距离
queue<DATA> q;
q.push({
y, 0});
dis[y]=0;
while (false == q.empty()) {
DATA curr = q.front();
q.pop();
//合法性判断
if (dis.find(curr.val) != dis.end() && dis[curr.val]<curr.dis) {
continue;
}
dis[curr.val] = curr.dis;//保存新的距离
ans = min(ans, curr.dis+abs(x-curr.val));
if (curr.val <=1) {
//到头了
continue;
}
DATA next;
//除以2
if (0==curr.val%2) {
//偶数
next.val = curr.val/2;
next.dis = curr.dis+1;
q.push(next);
} else {
//奇数
//increase 1
next.val = (curr.val+1)/2;
next.dis = curr.dis+2;
q.push(next);
//decrease 1
next.val = (curr.val-1)/2;
next.dis = curr.dis+2;
q.push(next);
}
}
cout<<ans<<"\n";
}
int main() {
#ifndef __LOCAL
//这部分代码需要提交到OJ,本地调试不使用
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
#endif
ll x, y;
cin>>x>>y;
if (x==y) {
cout<<"0\n";
return 0;
} else if (x>y) {
cout<<x-y<<"\n";
return 0;
}
bfs(x, y);
#ifdef __LOCAL
//这部分代码不需要提交到OJ,本地调试使用
system("pause");
#endif
return 0;
}
进一步优化
写道这里,感觉本题完全可以用数学的方法来实现。我想想看,目前还没有头绪。