廣度優先搜索
廣度優先搜索算法如下:(用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