【ACM专题训练】广度优先搜索

哆啦A梦的时光机

哆啦A梦有一个神奇的道具:时光机。坐着它,大雄和他的伙伴们能穿越时空,回到过去或者去到未来。

                                

 

有一天,大雄和他的伙伴们想穿越时空进行探险,可是时光机却出了一点故障,只能进行有限的时空穿越操作。大雄他们需要从现在出发,到达一个目标时间点进行探险,结束后再返回到现在,他们希望尽可能减少时光机的操作次数,你能帮助他们吗?

假设大雄和他的伙伴们出发的时间点(现在)为S(0<S<1,000,000),希望到达的时间点(目标)为 T(0<T<1,000,000),已知时光机可以进行如下的时空穿越操作(X 为正整数):

可以从任意时刻X穿越到 X+1 或者 X−1 时刻

可以从任意时刻X穿越到X×2 时刻

当 X 为偶数时,可以从 X 时刻穿越到 X/2 时刻

请问,大雄和他的伙伴们从 S 时刻出发,先到达 T 时刻,再回到 S 时刻最少需要多少次时空穿越操作?

输入格式

输入的第一个数是一个正整数 N,表示测试数据一共有 N 组(0<N<20)。之后有 N 行,每一行包含两个正整数 S 和 T,表示出发和到达时间点。S not = T

输出格式

输出包括N行,每一行一个正整数,表示每组测试数据对应的最少时光机操作次数。

样例解释

对于 S=5,T=17:
操作如下:5->4->8->16->17->16->8->4->5

对于 S=4,T=8:操作如下:4->8->4

样例输入复制

2
5 17
4 8

样例输出复制

8
2
#include <cstdio>
#include <queue>
#include <iostream>
#include <cstring>
using namespace std;
queue<pair<int,int> > q;
bool vis[1000001];
int main(){
    int n,s,t,now,step;
    cin >> n;
    while(n--){
        cin >> s >> t;
        while(!q.empty()){
            q.pop();
        }
        memset(vis,0,sizeof(vis));
        q.push(make_pair(s,0));
        vis[s] = true;
        while(!q.empty()){
            now = q.front().first;
            step = q.front().second;
            q.pop();
            if(now == t){
                cout << 2 * step << endl;
                break;
            }
            if(now - 1 > 0 && !vis[now -1]){
                vis[now - 1] = true;
        		q.push(make_pair(now - 1,step + 1));
            }
            if(now + 1 < 1000000 && !vis[now + 1]){
                vis[now + 1] = true;
        		q.push(make_pair(now + 1,step + 1));
            }
            if(now * 2 < 1000000 && !vis[now * 2]){
                vis[now * 2] = true;
        		q.push(make_pair(now * 2,step + 1));
            }
            if(now % 2 == 0 && !vis[now / 2]){
                vis[now / 2] = true;
        		q.push(make_pair(now / 2,step + 1));
            }
        }
    }
    return 0;
}

密码锁

现在一个紧急的任务是打开一个密码锁。密码由四位数字组成,每个数字从 1 到 9 进行编号。每次可以对任何一位数字加 1 或减 1。当将9加 1 时,数字将变为1,当1减 1 的时,数字将变为9。你也可以交换相邻数字,每一个行动记做一步。现在你的任务是使用最小的步骤来打开锁。注意:最左边的数字不与最右边的数字相邻。

输入格式

第一行输入四位数字,表示密码锁的初始状态。

第二行输入四位数字,表示开锁的密码。

输出格式

输出一个整数,表示最小步骤。

样例输入复制

1234
2144

样例输出复制

2
#include <cstdio>
#include <queue>
#include <cstring>
#include <iostream>
using namespace std;
struct node{
    int num[4],step;
}first,last;
int vis[11][11][11][11];
void bfs(){
    int i ;
    node a,next;
    queue<node> q;
    a = first;
    a.step = 0;
    q.push(a);
    memset(vis,0,sizeof(vis));
    vis[a.num[0]][a.num[1]][a.num[2]][a.num[3]] = 1;
    while(!q.empty()){
        a = q.front();
        q.pop();
        if(a.num[0] == last.num[0] &&a.num[1] == last.num[1] &&a.num[2] == last.num[2] &&a.num[3] == last.num[3]){
            cout << a.step;
            return ;
        }
        for(i = 0;i < 4;i++){
            next = a;
            next.num[i]++;
            if(next.num[i] == 10){
                next.num[i] = 1;
            }
            if(!vis[next.num[0]][next.num[1]][next.num[2]][next.num[3]]){
                vis[next.num[0]][next.num[1]][next.num[2]][next.num[3]] = 1;
                next.step ++;
                q.push(next);
            }
        }
        for(i = 0;i < 4;i++){
            next = a;
            next.num[i]--;
            if(next.num[i] == 0){
                next.num[i] = 9;
            }
            if(!vis[next.num[0]][next.num[1]][next.num[2]][next.num[3]]){
                vis[next.num[0]][next.num[1]][next.num[2]][next.num[3]] = 1;
                next.step ++;
                q.push(next);
            }
        }
        for(i = 0;i < 3;i++){
            next = a;
            next.num[i] = a.num[i + 1];
            next.num[i + 1] = a.num[i];

            if(!vis[next.num[0]][next.num[1]][next.num[2]][next.num[3]]){
                vis[next.num[0]][next.num[1]][next.num[2]][next.num[3]] = 1;
                next.step ++;
                q.push(next);
            }
        }
    }
}
int main(){
    int i,j,t;
    char s1[10],s2[10];
    scanf("%s%s",s1,s2);
    for(int i = 0;i < 4;i++){
        first.num[i] = s1[i] - '0';
        last.num[i] = s2[i] - '0';
    }
    bfs();
    return 0;
}

迷宫

给定一个 m×n 的迷宫,迷宫中有两个位置,蒜头君想从迷宫的一个位置走到另外一个位置,当然迷宫中有些地方是空地,蒜头君可以穿越,有些地方是障碍,他必须绕行,从迷宫的一个位置,只能走到与它相邻的 4 个位置中,当然在行走过程中,蒜头君不能走到迷宫外面去。令人头痛的是,蒜头君是个没什么方向感的人,因此,他在行走过程中,不能转太多弯了,否则他会晕倒的。我们假定给定的两个位置都是空地,初始时,蒜头君所面向的方向未定,他可以选择 4 个方向的任何一个出发,而不算成一次转弯。蒜头君能从一个位置走到另外一个位置吗?

输入格式

第 1行为一个整数 t (1≤t≤100),表示测试数据的个数,接下来为 t 组测试数据。

每组测试数据第 1 行为两个整数 m,n(1≤m,n≤100),分别表示迷宫的行数和列数,接下来 m 行,每行包括 n 个字符,其中字符'.'表示该位置为空地,字符'*'表示该位置为障碍,输入数据中只有这两种字符,每组测试数据的最后一行为 5 个整数k,x1​,y1​,x2​,y2​ (1≤k≤10,1≤x1​,x2​≤n,m1≤y1​,y2​≤m),其中 kk 表示蒜头君最多能转的弯数,(x1​,y1​),(x2​,y2​) 表示两个位置,其中x1​,x2​ 对应列,y1​,y2​ 对应行。

输出格式

每组测试数据对应为一行,若蒜头君能从一个位置走到另外一个位置,输出"yes",否则输出"no"

样例输入复制

2
5 5
...**
*.**.
.....
.....
*....
1 1 1 1 3
5 5
...**
*.**.
.....
.....
*....
2 1 1 1 3

样例输出复制

no
yes
#include <cstdio>
#include <iostream>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e2 + 9;
char mp[N][N];
int dx[] = {-1, 1, 0, 0};
int dy[] = {0, 0, -1, 1};
int n,m,k,a,b,x,y;
int vis[N][N];
struct node{
    int x,y,k,dir;
    node(){}
    node(int _x,int _y,int _k,int _dir){
        x = _x;
    	y = _y;
        k = _k;
        dir = _dir;
    }
};
void bfs(){
    queue<node> q;
    q.push(node(a,b,-1,-1));
    memset(vis,0x3f,sizeof(vis));
    vis[a][b] = -1;
    while(!q.empty()){
        node g = q.front();
        q.pop();
        if(g.x == x && g.y == y){
            cout << "yes";
            return ;
        }
        for(int i = 0;i <4;i++){
            int tx = g.x + dx[i];
            int ty = g.y + dy[i];
            int kk = g.k + (g.dir != i);
            if(tx >= 0 && tx < n && ty >= 0 && ty < m && mp[tx][ty] == '.' && kk <= k && vis[tx][ty] > kk){
                vis[tx][ty] = kk;
                q.push(node(tx,ty,kk,i));
            }
        }
    }
    cout << "no";
}
int main(){
    int t;
    cin >> t;
    while(t--){
        cin >> n >> m;
        for(int i = 0;i < n;i++){
            cin >> mp[i];
        }
        cin >> k >> b >> a >> y >> x;
    	a--,b--,x--,y--;
        bfs();
        cout << endl;
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值