AtCoder题解 —— AtCoder Beginner Contest 188 —— F - +1-1x2 —— BFS剪枝

题目相关

题目链接

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:

  1. increase the value written on the blackboard by 1 1 1;
  2. decrease the value written on the blackboard by 1 1 1;
  3. 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} 1X1018
  • 1 ≤ Y ≤ 1 0 18 1≤Y≤10^{18} 1Y1018
  • 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 2Y 的范围即可。但是由于 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=YX

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+NewYX。这样我们只需要取 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;
}

在这里插入图片描述

进一步优化

写道这里,感觉本题完全可以用数学的方法来实现。我想想看,目前还没有头绪。

猜你喜欢

转载自blog.csdn.net/justidle/article/details/112753195
今日推荐