《程序设计与算法》之【广度优先搜索】

广度优先搜索

广度优先搜索算法如下:(用QUEUE)

(1) 把初始节点S0放入Open表中;
(2) 如果Open表为空,则问题无解,失败退出;
(3) 把Open表的第一个节点取出放入 Closed表,并记该节点为n;
(4) 考察节点n是否为目标节点。若是,则得到问题的解,成功退出;
(5) 若节点n不可扩展,则转第(2)步;
(6) 扩展节点n,将其不在Closed表和 Open表中的子节点(判重)放入Open表的尾 部,并为每一个子节点设置指向父节点的指针 (或记录节点的层次),然后转第(2)步。

抓住那头牛(百练4001)

题目描述
农夫知道一头牛的位置,想要抓住它。农夫和牛都位于数轴上 ,农夫起始位于点N(0<=N<=100000),牛位于点 K(0<=K<=100000)。农夫有两种移动方式:
1、从X移动到X-1或X+1,每次移动花费一分钟
2、从X移动到2*X,每次移动花费一分钟
假设牛没有意识到农夫的行动,站在原地不动。农夫最少要 花多少时间才能抓住牛?
解答

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
int N, K;  // 农夫的位置和牛的位置
const int MAXN = 100000;
int visited[MAXN+10];   // 判重标记,visited[i] = true表示i已经扩展过
struct Step {
    int x; //位置
    int steps; // 到达x的步数
    Step(int xx, int s): x(xx), steps(s){}
};
queue<Step> q;    // 队列,即open表
int main() {
    cin >> N >> K;
    memset(visited, 0, sizeof(visited));
    q.push(Step(N, 0));
    visited[N] = 1;
    while (!q.empty()) {
        Step s = q.front();
        if (s.x == K) {
            cout << s.steps << endl;
            return 0;
        }
        else {
            if (s.x - 1 >= 0 && !visited[s.x-1]) {
                q.push(Step(s.x-1, s.steps+1));
                visited[s.x-1] = 1;
            }
            if (s.x + 1 <= MAXN && !visited[s.x+1]) {
                q.push(Step(s.x+1, s.steps+1));
                visited[s.x+1] = 1;
            }
            if (s.x * 2 <= MAXN && !visited[s.x*2]) {
                q.push(Step(s.x*2, s.steps+1));
                visited[s.x*2] = 1;
            }
        }
        q.pop();
    }
    return 0;
}

迷宫问题(百练4127)

#include <iostream>
#include <cstring>
#include <queue>
#include <cstdio>
using namespace std;
const int N = 6;
int maze[N][N];
int delta[] = {-1, 0, 1};
struct Node {
    int x, y;
    int steps;
    Node(int xx = 0, int yy = 0) : x(xx), y(yy) {}
};
queue<Node> q;   // 广搜的队列
Node priNode[N][N];
int visited[N][N];
void printPath(int r, int c) {
    if (r == 0 && c == 0)
        return;

    printPath(priNode[r][c].x, priNode[r][c].y);
    printf("(%d, %d)\n", priNode[r][c].x, priNode[r][c].y);
}
int main() {
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            cin >> maze[i][j];
            visited[i][j] = 0;
        }
    }
    Node start(0, 0);
    q.push(start);
    visited[0][0] = 1;
    while (!q.empty()) {
        Node p = q.front();
        int xx = p.x, yy = p.y;
        if (xx == n-1 && yy == n-1)
            break;
        int tx, ty;
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if (i == j)
                    continue;
                tx = xx + delta[i];
                ty = yy + delta[j];

                if (tx >= 0 && tx < n && ty >= 0 && ty < n) {

                    if (maze[tx][ty] == 0 && visited[tx][ty] == 0) {
                        visited[tx][ty] = 1;
                        q.push(Node(tx, ty));
                        priNode[tx][ty].x = xx;
                        priNode[tx][ty].y = yy;

                    }
                }
            }
        }
        q.pop();
    }
    printPath(n-1, n-1);
    printf("(%d, %d)\n", n-1, n-1);
    return 0;
}

鸣人和佐助(百练6044)

问题描述
已知一张地图(以二维矩阵的形式表示)以及佐助和鸣人的位置。地图上的每个位置都可以走到,只不过有些位置上有大蛇丸的手下(#),需要先打败大蛇丸的手下才能到这些位置。
鸣人有一定数量的查克拉,每一个单位的查克拉可以打败一个大蛇丸的手下。假设鸣人可以往上下左右四个方向移动, 每移动一个距离需要花费1个单位时间,打败大蛇丸的手下不需要时间。如果鸣人查克拉消耗完了,则只可以走到没有大蛇丸手下的位置,不可以再移动到有大蛇丸手下的位置。
佐助在此期间不移动,大蛇丸的手下也不移动。请问,鸣人要追上佐助最少需要花费多少时间?

4 4 1
# @ # #
* * # #
# # # +
* * * *

解题思路

  • 状态定义为:(r,c,k),鸣人所在的行,列和查克拉数量
  • 如果队头节点扩展出来的节点是有大蛇手下的节点, 则其 k 值比队头的k要减掉 1。* 如果队头节点的查克 拉数量为0,则不能扩展出有大蛇手下的节点。
  • 为了不超时,要进行剪枝,用数组记录下走到每个点时所剩查克拉数最大为多少,当再次经过该点时,如果剩下的查克拉数小于最大的数,就不用继续扩展了

解答

#include <iostream>
#include <cstring>
#include <queue>
#include <cstdio>
using namespace std;
const int MAXN = 210;
char map[MAXN][MAXN];
int delta[] = {-1, 0, 1};
struct Node {
    int x, y;
    int k;
    int time;
    Node(int xx = 0, int yy = 0, int kk = 0, int time = 0) : x(xx), y(yy), k(kk), time(time) {}
};
queue<Node> q;      // 广搜的队列
int minK[MAXN][MAXN];   // 到达某点所剩的查克拉最大为多少
int minT = 1 << 30;
int main() {
    int m, n;
    cin >> m >> n;
    int startX = 0, startY= 0, endX = 0, endY = 0, k;
    cin >> k;
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            cin >> map[i][j];
            if (map[i][j] == '@') {
                startX = i;
                startY = j;
            }
            if (map[i][j] == '+') {
                endX = i;
                endY = j;
            }
            minK[i][j] = -1;
        }
    }
    Node start(startX, startY, k, 0);
    q.push(start);
    while (!q.empty()) {
        Node p = q.front();
        int xx = p.x, yy = p.y;
        if (map[xx][yy] == '+') {
            minT = p.time;
            break;
        }
        int tx, ty;
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if (i == j)
                    continue;
                tx = xx + delta[i];
                ty = yy + delta[j];

                if (tx >= 0 && tx < n && ty >= 0 && ty < n) {
                    if (minK[tx][ty] < p.k) {
                        if (p.k >= 0 && map[tx][ty] == '#') {
                            q.push(Node(tx, ty, p.k-1, p.time+1));
                            minK[tx][ty] = p.k-1;
                        } else if (map[tx][ty] == '*' || map[tx][ty] == '+') {
                            q.push(Node(tx, ty, p.k, p.time+1));
                            minK[tx][ty] = p.k;
                        }
                    }
                }
            }
        }
        q.pop();
    }
    if (minT == (1 << 30))
        cout << "-1" << endl;
    else
        cout << minT << endl;
    return 0;
}

八数码问题(百练1077)

问题描述
八数码问题是人工智能中的经典问题
有一个3*3的棋盘,其中有0-8共9个数字,0表示空格, 其他的数字可以和0交换位置。求由初始状态到达目标状态的步数最少的解。

1 2 3
4 5 6
7 8 0

猜你喜欢

转载自blog.csdn.net/beashaper_/article/details/80549504