搜索2(一、马的遍历 二、 求细胞数量 三、01迷宫 四、奇怪的电梯 五、海战 六、路障)

一、马的遍历

题目描述
有一个n*m的棋盘(1<n,m<=400),在某个点上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步
输入
一行四个数据,棋盘的大小和马的坐标

输出
一个n*m的矩阵,同一行元素之间用空格分离。代表马到达某个点最少要走几步。不能到达则输出-1。
样例输入
3 3 1 1
样例输出
0 3 2
3 -1 1
2 1 4
题目分析
本题思路在于bfs,找出马的八个可以走的下一个位置,使用cnt数组记录步数,每次的循环里都使用cnt[tx][ty] = cnt[sx][sy] + 1;来更新到达某一位置的步数。注意使用队列的思想,先把初始位置入队,之后在循环内把初始位置出队并且记录,之后在走到下一位置之后,把下一元素的坐标入队,以此类推,最后就能得到到每个位置的cnt了。

#include <bits/stdc++.h>
#include <queue>
using namespace std;
int m, n, a, b;
int s[8][2] = {1, 2, 2, 1, 1, -2, 2, -1, -1, 2, -2, 1, -1, -2, -2, -1};//马可以到达的八个位置int visit[500][500], cnt[500][500];
queue<int> q;
void bfs(int sx, int sy)
{
    q.push(sx);
    q.push(sy);//sx,sy也就是现在的坐标,入队
    int tx, ty;//代表下一步的位置
    visit[sx][sy] = 1;//记录已经走过sx,sy
    while (!q.empty()) {//当队列不空的时候
        sx = q.front();//sx等于队首元素
        q.pop();//队首元素出队
        sy = q.front();//sy等于队首元素
        q.pop();//队首元素出队
        for (int k = 0; k < 8; k++) {//马可以到达的八个位置遍历
            tx = sx + s[k][0];
            ty = sy + s[k][1];//更新马下一步要到达的位置
            if (tx >= 1 && tx <= m && ty >= 1 && ty <= n && visit[tx][ty] == 0) {//如果在棋盘上而且没走过
                visit[tx][ty] = 1;//走这个位置
                cnt[tx][ty] = cnt[sx][sy] + 1;//走过的步数++
                q.push(tx);//队中元素出队
                q.push(ty);
            }
        }
    }
}
int main()
{
    cin >> m >> n;
    int ar[m + 1][n + 1];
    cin >> a >> b;
    memset(cnt, -1, sizeof(cnt));
memset(visit, 0, sizeof(visit));
memset(ar, 0, sizeof(ar));//初始化三个数组
    cnt[a][b] = 0;//代表开始的位置到开始的位置步数是0
    bfs(a, b);//搜索
    for(int i = 1; i <= m; i++) {
        for (int j = 1; j <= n; j++)
            cout << cnt[i][j] << " ";//输出到达每一个位置的步数
        cout << endl;
    }
    return 0;
}

二、 求细胞数量

题目描述
一矩形阵列由数字 0 到 9 组成,数字 1 到 9 代表细胞,细胞的定义为沿细胞数字上下左右若还是细胞数字则为同一细胞,求给定矩形阵列的细胞个数。

输入
第一行两个整数代表矩阵大小 n 和 m。 接下来 n 行,每行一个长度为 m 的只含字符 0 到 9 的字符串,代表这个n×m 的矩阵。
输出
一行一个整数代表细胞个数。

样例输入
4 10
0234500067
1034560500
2045600671
0000000089
样例输出
4
提示
0 < m, n <= 400
题目分析
本题思路bfs,但是这个不是记录到每个位置的步数,而是连通域的个数,这就需要在bfs内部加cnt记录,同时在main函数里对每一个等于一的元素进行搜索,遍历他周围的细胞,直到遍历结束。

#include <bits/stdc++.h>
#include <queue>
using namespace std;
int m, n, cnt = 0;
int s[4][2] = {-1, 0, 0, 1, 1, 0, 0, -1};//八个不同的下一步的遍历方式
char ar[500][500];//由于输入的数据中间没有空格,所以需要设置为char类型
int arr[500][500];//用来储存是否有细胞
queue<int> q;
void bfs(int sx, int sy) {
    q.push(sx), q.push(sy);//初始位置入队
    cnt++;//细胞数目++
    int tx, ty;//下一步位置
    arr[sx][sy] = 0;//表示已经遍历过这个位置
    while (!q.empty()) {//当队列不空时
        sx = q.front();//sx,sy分别为现在所处位置的坐标
        q.pop();
        sy = q.front();
        q.pop();//出队
        for (int k = 0; k < 4; k++) {//四个可能的遍历方式
            tx = sx + s[k][0];
            ty = sy + s[k][1];//更新下一步位置
            if (tx >= 1 && tx <= m && ty >= 1 && ty <= n && arr[tx][ty] == 1) {//位置符合条件
                arr[tx][ty] = 0;//走这个位置
                q.push(tx), q.push(ty);//现在的坐标入队
            }       }    }}
int main() {
    cin >> m >> n;
    memset(arr, 1, sizeof(arr));//初始化arr数组(设为每个位置都有细胞)
    for (int i = 1; i <= m; i++)
    for (int j = 1; j <= n; j++) {
        cin >> ar[i][j];
        if (ar[i][j] == '0')   arr[i][j] = 0;//输入为‘0’时代表这个地方没有细胞,更新arr[i][j]
    }
    for (int i = 1; i <= m; i++)
        for (int j = 1; j <= n; j++) {
        if(arr[i][j] == 1)   bfs(i, j);}//当这个位置有细胞时,搜索    
    cout << cnt << endl;//输出细胞数
    return 0;}

三、01迷宫

题目描述
有一个仅由数字0与1组成的n×n格迷宫。若你位于一格0上,那么你可以移动到相邻4格中的某一格1上,同样若你位于一格1上,那么你可以移动到相邻4格中的某一格0上。 你的任务是:对于给定的迷宫,询问从某一格开始能移动到多少个格子(包含自身)。
输入
第1行为两个正整数n,m。 下面n行,每行n个字符,字符只可能是0或者1,字符之间没有空格。
接下来m行,每行2个用空格分隔的正整数i,j,对应了迷宫中第i行第j列的一个格子,询问从这一格开始能移动到多少格。
输出
m行,对于每个询问输出相应答案。

样例输入
2 2
01
10
1 1
2 2
样例输出
4
4
提示
n <= 400
题目分析
本题思路仍然为bfs,但需要当前位置和下一位置的值不同,也就是ar[tx][ty] != ar[sx][sy],同时在while循环下输入询问坐标并搜索,使用cn代表能到的格子数,在bfs里面到达下一位置时cn增加1即可。

#include <bits/stdc++.h>
#include <queue>
using namespace std;
int m, n, a, b, cn;
int s[4][2] = {-1, 0, 0, 1, 1, 0, 0, -1}, visit[500][500], ar[500][500];
char car[500][500];	
queue<int> q;
void bfs(int sx, int sy) {
    memset(visit, 0, sizeof(visit));
    q.push(sx), q.push(sy);//初始位置入队
    int tx, ty;//定义下一步位置的坐标
    visit[sx][sy] = 1;//走过这一位置
    while (!q.empty()) {//当队列不空的时候
        sx = q.front();   q.pop();
        sy = q.front();   q.pop();//队首元素就是现在的位置,sx,sy记录现在的位置
        for (int k = 0; k < 4; k++) {//遍历四个方向
            tx = sx + s[k][0];
            ty = sy + s[k][1];//更新下一步的位置
            if (tx >= 1 && tx <= n && ty >= 1 && ty <= n && (visit[tx][ty] == 0) && (ar[tx][ty] != ar[sx][sy])) {//如果在迷宫里而且没被访问过而且和上一步的值不一样
                visit[tx][ty] = 1;//访问这个位置
                cn += 1;//能移动到的格子数加1
                q.push(tx), q.push(ty);//当前位置的坐标入队
            }        }    }}
int main() {
    cin >> m >> n;
    for (int i = 1; i <= n; i++)
    for (int j = 1; j <= n; j++) {
        cin >> car[i][j];//输入字符串
        ar[i][j] = car[i][j] - '0';//转化为int类型的数字
    }
    while (m --) {
        cin >> a >> b;//输入想要询问的位置
        cn = 1;//一定可以到现在的格子,所以先赋值为1
        bfs(a, b);//搜索
        cout << cn << endl;}//输出可以到达的格子数
    return 0;}

四、奇怪的电梯

题目描述
有一天我做了一个梦,梦见了一种很奇怪的电梯。大楼的每一层楼都可以停电梯,而且第i层楼(1≤i≤N)上有一个数字Ki(0≤Ki≤N)。电梯只有四个按钮:开,关,上,下。上下的层数等于当前楼层上的那个数字。当然,如果不能满足要求,相应的按钮就会失灵。例如:3,3,1,2,5代表了Ki(K1=3,K2=3,…),从1楼开始。在1楼,按“上”可以到4楼,按“下”是不起作用的,因为没有−2楼。那么,从A楼到B楼至少要按几次按钮呢?
输入
共二行。
第一行为3个用空格隔开的正整数,表示N,A,B(1≤N≤200,1≤A,B≤N)。
第二行为N个用空格隔开的非负整数,表示Ki。
输出
输出一行,即最少按键次数,若无法到达,则输出−1。
样例输入
5 1 5
3 3 1 2 5
样例输出
3
题目分析
本题不需要设坐标,只有一个坐标轴,但是需要分情况讨论,注意向上向下两种情况都需要考虑而且都入队,之后对两种情况分别分析,如果向上向下都在范围内,就都入队,之后总会有pop出去的时候,但是注意cnt[i]只能更新一次,所以第一次到达b位置会对cnt[b]进行更新,不用担心最后结果不正确。

#include <bits/stdc++.h>
#include <queue>
using namespace std;
int n, a, b;
int cnt[500], ar[500];
queue<int> q;
void bfs(int s)
{
    q.push(a);//a入队,也就是当前楼层
    int e;
    while (!q.empty()) {//当队不空的时候
        e = q.front();//e等于队首元素
        if (e + ar[e] > 0 && e + ar[e] <= n && cnt[e + ar[e]] == -1) {//如果向上走符合要求且没到过第e+ar[e]层楼的时候
            cnt[e + ar[e]] = cnt[e] + 1;//a到e+ar[e]需要按的按钮数等于a到e需要按的按钮数加1
            q.push(e + ar[e]);//当前楼层入队
        }
        if (e - ar[e] > 0 && e - ar[e] <= n && cnt[e - ar[e]] == -1) {//思路同上,向下走
            cnt[e - ar[e]] = cnt[e] + 1;
            q.push(e - ar[e]);
        }
        q.pop();//弹出队首元素(也就是最开始在队里的元素)
    }//本题把两种情况都入队,之后对每一种情况都分别分析
}
int main()
{
    cin >> n >> a >> b;//一共n层楼,从a到b
    for (int i = 1; i <= n; i++)
        cin >> ar[i];//输入每层楼可以上下移动的楼层数
    memset(cnt, -1, sizeof(cnt));//初始化(都不能到达)
    cnt[a] = 0;//a到a需要按按钮的次数是0
    bfs(a);//搜索
    cout << cnt[b] << endl;//输出a到b需要按按钮的次数
    return 0;
}

五、海战

题目描述
在峰会期间,武装部队得处于高度戒备。警察将监视每一条大街,军队将保卫建筑物,领空将布满了F-2003飞机。此外,巡洋船只和舰队将被派去保护海岸线。不幸的是因为种种原因,国防海军部仅有很少的几位军官能指挥大型海战。因此,他们考虑培养一些新的海军指挥官,他们选择了“海战”游戏来帮助学习。
在这个著名的游戏中,在一个方形的盘上放置了固定数量和形状的船只,每只船却不能碰到其它的船。在这个题中,我们仅考虑船是方形的,所有的船只都是由图形组成的方形。编写程序求出该棋盘上放置的船只的总数。
输入
输入文件头一行由用空格隔开的两个整数R和C组成,1<=R,C<=1000,这两个数分别表示游戏棋盘的行数和列数。接下来的R行每行包含C个字符,每个字符可以为“#”,也可为“.”,“#”表示船只的一部分,“.”表示水。
输出
为每一个段落输出一行解。如果船的位置放得正确(即棋盘上只存在相互之间不能接触的方形,如果两个“#”号上下相邻或左右相邻却分属两艘不同的船只,则称这两艘船相互接触了)。就输出一段话“There are S ships.”,S表示船只的数量。否则输出“Bad placement.”。
样例输入
6 8
…#.#
##…#
##…#
…#
#…#
#…#…#
样例输出
There are 5 ships.
题目分析
本题思路为bfs,但是需要再加一个函数判断两个方形是否接触,可以发现,如果在上左原地以及右上四个位置组成的菱形区域内有且仅有三个是#时,两个方形接触。判断完毕之后,注意需要输出船的数量,这个和计算细胞数目那道题有相似的地方,先更新数组为01数组,1代表船。在main函数里写for循环,如果某一位置的元素是1,就进行搜索,并且遍历他周围的元素,注意使用visit数组记录是否已经遍历过这个位置的船。注意cnt只在bfs函数最外面加(因为要计算的是船的数目)

#include <bits/stdc++.h>
#include <queue>
using namespace std;
int m, n, cnt = 0;
int s[6][2] = {-1, 0, 0, -1, 0, 1, 1, 0, 0, 0, 1, 1}, arr[500][500];//左上右下及原地右上
char ar[500][500];
queue<int> q;
bool fang(int sx, int sy) {//判断是不是两条船接触
    int count = 0;
    for (int i = 2; i < 6; i++) {//如果四个位置中有三个是#,那么接触了(上 左 原地 右上)
        if (arr[sx + s[i][0]][sy + s[i][1]] == '#')
            count++;
    }
    if (count == 3)  return 0;
    return 1;
}
void bfs(int sx, int sy) {
    q.push(sx), q.push(sy);//当前位置入队
    cnt++;//船只数量++
    int tx, ty;//定义下一个位置
    arr[sx][sy] = 0;//标记已经走过这个位置
    while (!q.empty()) {//当队列不空的时候
        sx = q.front();   q.pop();//取队头元素作为当前位置,并且队头元素出队
        sy = q.front();   q.pop();
        for (int k = 0; k < 4; k++) {//在四个方向上遍历
            tx = sx + s[k][0];
            ty = sy + s[k][1];//更新下一步的位置
            if (tx >= 1 && tx <= m && ty >= 1 && ty <= n && arr[tx][ty] == 1) {//如果下一步符合条件
                arr[tx][ty] = 0;//走这个位置
                q.push(tx), q.push(ty);//这个位置入队
            }
        }
    }
}
int main()
{
    cin >> m >> n;
    memset(arr, 1, sizeof(arr));//初始化arr(代表都有船)
    for (int i = 1; i <= m; i++)
    for (int j = 1; j <= n; j++) {
        cin >> ar[i][j];
        if (ar[i][j] == '.')  arr[i][j] = 0;//更新,没有船的位置arr更新为0
    }
    for (int i = 1; i <= m; i++)
    for (int j = 1; j <= n; j++) {
        if(i <= m && j <= n && fang(i, j) == 0) {//当船在区域内但是接触
            cout << "Bad placement." << endl;
            return 0;
        }
    }//那么剩下的情况都是船在区域内而且没有接触
    for (int i = 1; i <= m; i++)
    for (int j = 1; j <= n; j++) {
        if(arr[i][j] == 1)//当某一位置有船
           bfs(i, j);//搜索
    }
    cout << "There are " << cnt << " ships." << endl;//输出船的数量
    return 0;
}

六、路障

题目描述
B君站在一个n×n的棋盘上。最开始,B君站在(1,1)这个点,他要走到(n,n)这个点。 B君每秒可以向上下左右的某个方向移动一格,但是很不妙,C君打算阻止B君的计划。
每秒结束的时刻,C君会在(x,y)上摆一个路障。B君不能走在路障上。
B君拿到了C君准备在哪些点放置路障。所以现在你需要判断,B君能否成功走到(n,n)。
保证数据足够弱:也就是说,无需考虑“走到某处然后被一个路障砸死”的情况,因为答案不会出现此类情况。

输入
首先是一个正整数T,表示数据组数。
对于每一组数据:
第一行,一个正整数n。
接下来2n-2行,每行两个正整数x和y,意义是在那一秒结束后,(x,y)将被摆上路障。
输出
对于每一组数据,输出Yes或No,回答B君能否走到(n,n)。

样例输入
2

2
1 1
2 2

5
3 3
3 2
3 1
1 2
1 3
1 4
1 5
2 2
样例输出
Yes
Yes
题目分析
本题需要记录走的步数,以及在第多少秒在什么位置放路障,可以使用ar[a][b] = j;非常巧妙地得到在第j秒在(a, b)位置当一个路障。同时在判断能不能走下一步时,需要再加上time[tx][ty]和ar[tx][ty]的比较,最后注意在visit一个位置之后,更新时间数组tim[tx][ty] = tim[sx][sy]+1。

#include <bits/stdc++.h>
#include <queue>
using namespace std;
int m, n;
int s[4][2] = {-1, 0, 0, 1, 1, 0, 0, -1};//四个方向
int ar[1010][1010], tim[1010][1010];//ar记录在第多少秒会在(i,就)放路障,tim记录时间(走了多少步)
bool visit[1010][1010];
bool bfs(int sx, int sy) {
    queue<int> q;
    memset(visit, 0, sizeof visit);
    memset(tim, 0, sizeof tim);//初始化数组
    q.push(sx), q.push(sy);//初始位置入队
    int tx, ty;//定义下一步位置
    visit[sx][sy] = 1;//标记已经走过初始位置
    while (!q.empty()) {//当队列不空的时候
        sx = q.front();   q.pop();
        sy = q.front();   q.pop();//sx,sy等于队首元素,队首元素出队
        for (int k = 0; k < 4; k++) {//遍历四个方向
            tx = sx + s[k][0];
            ty = sy + s[k][1];//更新下一步的位置
            if (tx > 0 && tx <= m && ty > 0 && ty <= n && visit[tx][ty] == 0)
                continue;//当下一步的位置在范围内而且没有被访问过就继续
            if ((ar[tx][ty] == 0) || (ar[tx][ty] > tim[tx][ty])) {//如果这个地方不放路障或者放路障的时间大于到达这个位置所用的时间
                visit[tx][ty] = 1;//走这个位置
                q.push(tx), q.push(ty);//这个位置入队
                tim[tx][ty] = tim[sx][sy] + 1;//时间更新,加1
                if (tx == n && ty == n) {//如果已经到达了终点
                    if ((ar[n][n] == 0) || (ar[n][n] >= tim[n][n]))
  return 1;//如果(n,n)处不放路障或者在(n,n)处放路障的时间大于走到的时间,返回1
                    else return 0;//否则返回0
                }
            }
        }
    }
    return 0;//需要再加一个return 0,意思就是到不了终点,需要返回0
}
int main()
{
    int k;
    cin >> k;
    while (k --) {
        cin >> n;
        if (n == 1) {//当他要到的位置是1,1;也就是说不用动,直接输出yes
            cout << "Yes" << endl;
            return 0;
        }
        memset(ar, 0, sizeof ar);//初始化数组ar
        for(int i = 1; i <= 2 * n - 2; i++) {//将要在第i秒的时候在(a,b)位置上放路障
            int a, b;
            cin >> a >> b;//输入位置
            ar[a][b] = i;
        }
        if (bfs(1, 1)) {//从(1,1)开始遍历搜索,如果能到达就输出yes
            cout << "Yes" << endl;
        }
        else cout << "No" << endl;//不能到达就输出no
    }
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值