《程序設計與算法》之【廣度優先搜索】

廣度優先搜索

廣度優先搜索算法如下:(用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
發佈了97 篇原創文章 · 獲贊 27 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章