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