当青训营遇上码上掘金 之主题 3:寻友之旅

78 阅读3分钟

当青训营遇上码上掘金

之主题 3:寻友之旅

本文章是对“「青训营 X 码上掘金」主题创作活动”的第三题:寻友之旅的讲解,思路来源于作者本人,若有问题欢迎指出。

题目分析

首先,我们可以简单的分析一下小青的行动方法:

  • 走路:使从x位置转移到x+1位置或者x-1位置。
  • 坐公交:使从x位置转移到x*2位置。

位置范围是0~100000。

这两种方法的每次行动耗费时间都是1分钟。值得一提的是,坐公交不能坐回去,然而,这个条件也并没有什么用——因为乘法不像减法一样会使数字变小嘛!

解题主要思路

bfs

天不生我暴搜,万古算法如长夜!只要稍微接触学过算法的同学,看到此题的第一印象就是暴搜。

然而,毋庸置疑的是,如果不限制范围,那么小青每次从每个位置可以到达的新位置都是3个,这使得考虑情况会以指数级增长——然后爆栈...

但是,机智的我们发现了,由于位置范围是给定的:0~100000,所以我们直接限定位置范围,然后让每个位置标记一下最先到的地方,下次就不要再到一次这个地方了,毕竟最先到的肯定有最优的最小时间嘛!这样,时间复杂度就降到了惊人的O(m),也就是O(1e5)!

看起来,本题的完美解法就诞生了。

dijkstra或spfa

众所周知,spfa死了spfa只适用于随机数据情况,其他情况下效率更差,所以本题不讨论它。

那么,dijkstra在本题效果怎么样?本人无意间看到,有许多同学使用这个方法解答这道题,然而,哪怕是用上了堆优化,本题的时间复杂度也会达到O(mlog(m)),也就是大约1e6的级别,太烂辣!

作者本人的小优化

由于,我们可以观察到,小青从起点走到终点的行动路线具有对称性。因此,虽然规定了小码不能移动,但是我们仍然可以设置让小码、小青同时行动,以加快bfs搜索的效率,减少废枝判断。

是的,这也就是ACM中的双向BFS搜索优化

完整代码如下:

#include <bits/stdc++.h>
#define fi first
#define se second
#define endl '\n'
#define butane cout<<"test"<<endl;
#define lowbit(x) x&-x
using namespace std;

const int maxv=1e6+100;
const int mod=2008;
const int maxNum=0x7fffffff-10;
const double eps=1e-8;
const double PI=3.1415926535;
typedef long long LL;

inline int read(){
    int x=0, f=1;
    char ch=getchar();
    while (ch < '0' || ch > '9'){
        if (ch == '-') f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9'){
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
    //    return x;
}
inline void write(__int128 x){
    if (!x) putchar('0');
    char F[200];
    __int128 tmp = x > 0 ? x : -x;
    if (x < 0) putchar('-');
    int cnt = 0;
    while (tmp > 0) F[cnt++] = tmp % 10 + '0',tmp /= 10;
    while (cnt > 0) putchar(F[--cnt]);
    // putchar('\n');
    // putchar(' ')
}

//双向广度优先搜索
//理论上复杂度更低,为O(1e5)
//此题广度搜索比dijkstra或spfa更优秀
//应该有数论解法
bool visS[maxv],visT[maxv];
int cntS[maxv],cntT[maxv];
bool judge(int x){return x>=0&&x<=100000;}
int bfs(int s,int t){
    int ans=maxNum;
    queue<pair<int,int> > qs,qt;
    qs.push({s,0}),qt.push({t,0});
    while(qs.size()||qt.size()){
        if(qs.size()){
            int sTop=qs.front().first,sTopCnt=qs.front().second;qs.pop();
            visS[sTop]=1;
            if(judge(sTop-1)&&!visS[sTop-1]){
                if(visT[sTop-1]) ans=min(ans,sTopCnt+cntT[sTop-1]);
                qs.push({sTop-1,cntS[sTop-1]=sTopCnt+1}),visS[sTop-1]=1;
            }
            if(judge(sTop+1)&&!visS[sTop+1]){
                if(visT[sTop+1]) ans=min(ans,sTopCnt+cntT[sTop+1]);
                qs.push({sTop+1,cntS[sTop+1]=sTopCnt+1}),visS[sTop+1]=1;
            }
            if(judge(sTop*2)&&!visS[sTop*2]){
                if(visT[sTop*2]) ans=min(ans,sTopCnt+cntT[sTop*2]);
                qs.push({sTop*2,cntS[sTop*2]=sTopCnt+1}),visS[sTop*2]=1;
            }
        }
        if(qt.size()){
            int tTop=qt.front().first,tTopCnt=qt.front().second;qt.pop();
            visT[tTop]=1;
            if(judge(tTop-1)&&!visT[tTop-1]){
                if(visS[tTop-1]) ans=min(ans,tTopCnt+cntS[tTop-1]);
                qt.push({tTop-1,cntT[tTop-1]=tTopCnt+1}),visT[tTop-1]=1;
            }
            if(judge(tTop+1)&&!visT[tTop+1]){
                if(visS[tTop+1]) ans=min(ans,tTopCnt+cntS[tTop+1]);
                qt.push({tTop+1,cntT[tTop+1]=tTopCnt+1}),visT[tTop+1]=1;
            }
            if(tTop%2==0&&judge(tTop/2)&&!visT[tTop/2]){
                if(visS[tTop/2]) ans=min(ans,tTopCnt+cntS[tTop/2]);
                qt.push({tTop/2,cntT[tTop/2]=tTopCnt+1}),visT[tTop/2]=1;
            }
        }
    }
    return ans;
}
signed main(){
    int n=read(),k=read();
    write(bfs(n,k));
    return 0;
}

xTUzIwKt - 码上掘金 (juejin.cn)