预备知识
队列
先进先出(FIFO),队头删除元素,队尾加入元素。
二叉树“层次遍历”
从上到下、从左到右的顺序进行遍历。
使用算法实现思想:
建立一个队列用于存放节点的信息。当访问到一个节点时(队列不空时),先访问该节点,再将该节点的左右儿子放入队列。再根据这个队列依次从前向后访问。(搜索的本质就是暴力枚举)
本质上就是描述出:起始状态、目标状态以及状态转移规则!
经典例题
奇怪的电梯
解:使用楼层的层次遍历(根据每一层楼的树状结构可以形成一个树状结构),同时使用一个数组来标记一个楼层是否曾经到达过(用来优化(剪枝)),当到达目标楼层后就停止迭代。(使用楼层以及跳转次数作为一个特定的状态)
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
int N, k[201], visit[201], start, target, decision;
struct position
{
int level;
int steps;
};
int main()
{
while(scanf("%d", &N), N)
{
decision = 0;
scanf("%d%d", &start, &target);
memset(k, 0, sizeof(k));
memset(visit, 0, sizeof(visit));
for(int i = 1; i <= N; i++)
scanf("%d", &k[i]);
//BFS
position cur, next;
cur.level = start;
cur.steps = 0;
queue<position> Q;
Q.push(cur);
visit[start] = 1;
while(!Q.empty())
{
cur = Q.front();
Q.pop();
if(cur.level == target)
{
printf("%d\n", cur.steps);
decision = 1;
break;
}
next.level = cur.level + k[cur.level];
next.steps = cur.steps + 1;
if(next.level <= N)
if(visit[next.level] == 0)
{
visit[next.level] = 1;
Q.push(next);
}
next.level = cur.level - k[cur.level];
next.steps = cur.steps + 1;
if(next.level >= 1)
if(visit[next.level] == 0)
{
visit[next.level] = 1;
Q.push(next);
}
}
if(decision == 0) printf("-1\n");
}
return 0;
}
非常可乐
解:把状态定义为三个杯子中的可乐量以及步骤数,同时使用一个三维数组做标记(也可以使用二维),一直迭代到目标状态。
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
int s, n, m, visit[102][102][102], decision;
bool isSatisfied(double x, double y, double z) { return (x == y && z == 0) || (x == z && y == 0) || (z == y && x == 0); }
struct state
{
int cola1;
int cola2;
int cola3;
int steps;
};
int main()
{
while(scanf("%d%d%d", &s, &n, &m), s)
{
decision = 0;
memset(visit, 0, sizeof(visit));
state cur, next;
cur.cola1 = s;
cur.cola2 = 0;
cur.cola3 = 0;
cur.steps = 0;
visit[s][0][0] = 1;
queue<state> que;
que.push(cur);
while(!que.empty())
{
cur = que.front();
que.pop();
if(isSatisfied(cur.cola1, cur.cola2, cur.cola3))
{
decision = 1;
printf("%d\n", cur.steps);
break;
}
// 1 -> 2
if(cur.cola1 >= n-cur.cola2)
{
next.cola1 = cur.cola1 - (n - cur.cola2);
next.cola2 = n;
next.cola3 = cur.cola3;
}
else
{
next.cola1 = 0;
next.cola2 = cur.cola1 + cur.cola2;
next.cola3 = cur.cola3;
}
if(visit[next.cola1][next.cola2][next.cola3] == 0)
{
visit[next.cola1][next.cola2][next.cola3] = 1;
next.steps = cur.steps + 1;
que.push(next);
}
// 1 -> 3
if(cur.cola1 >= m-cur.cola3)
{
next.cola1 = cur.cola1 - (m - cur.cola3);
next.cola3 = m;
next.cola2 = cur.cola2;
}
else
{
next.cola1 = 0;
next.cola3 = cur.cola1 + cur.cola3;
next.cola2 = cur.cola2;
}
if(visit[next.cola1][next.cola2][next.cola3] == 0)
{
visit[next.cola1][next.cola2][next.cola3] = 1;
next.steps = cur.steps + 1;
que.push(next);
}
// 2 -> 1
if(cur.cola2 >= s-cur.cola1)
{
next.cola2 = cur.cola2 - (s - cur.cola1);
next.cola1 = s;
next.cola3 = cur.cola3;
}
else
{
next.cola2 = 0;
next.cola1 = cur.cola2 + cur.cola1;
next.cola3 = cur.cola3;
}
if(visit[next.cola1][next.cola2][next.cola3] == 0)
{
visit[next.cola1][next.cola2][next.cola3] = 1;
next.steps = cur.steps + 1;
que.push(next);
}
// 2 -> 3
if(cur.cola2 >= m-cur.cola3)
{
next.cola2 = cur.cola2 - (m - cur.cola3);
next.cola3 = m;
next.cola1 = cur.cola1;
}
else
{
next.cola2 = 0;
next.cola3 = cur.cola2 + cur.cola3;
next.cola1 = cur.cola1;
}
if(visit[next.cola1][next.cola2][next.cola3] == 0)
{
visit[next.cola1][next.cola2][next.cola3] = 1;
next.steps = cur.steps + 1;
que.push(next);
}
// 3 -> 1
if(cur.cola3 >= s-cur.cola1)
{
next.cola3 = cur.cola3 - (s - cur.cola1);
next.cola1 = s;
next.cola2 = cur.cola2;
}
else
{
next.cola3 = 0;
next.cola1 = cur.cola3 + cur.cola1;
next.cola2 = cur.cola2;
}
if(visit[next.cola1][next.cola2][next.cola3] == 0)
{
visit[next.cola1][next.cola2][next.cola3] = 1;
next.steps = cur.steps + 1;
que.push(next);
}
// 3 -> 2
if(cur.cola3 >= n-cur.cola2)
{
next.cola3 = cur.cola3 - (n - cur.cola2);
next.cola2 = n;
next.cola1 = cur.cola1;
}
else
{
next.cola3 = 0;
next.cola2 = cur.cola3 + cur.cola2;
next.cola1 = cur.cola1;
}
if(visit[next.cola1][next.cola2][next.cola3] == 0)
{
visit[next.cola1][next.cola2][next.cola3] = 1;
next.steps = cur.steps + 1;
que.push(next);
}
}
if(decision == 0) printf("NO\n");
}
return 0;
}
骑士移动问题
解:BFS的同时使用一个数组来描述骑士一步可以走到的地方,这样就可以使用一层循环来访问所有骑士从一个点到另一个点的可能。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
//int defaul = 0;
int n1, n2, visit[8][8];
int moves[8][2] = {{1,2}, {1,-2}, {2,1}, {2,-1}, {-1,2}, {-1,-2}, {-2,1}, {-2,-1}};
char m1, m2;
struct state
{
int row;
int col;
int steps;
};
int main()
{
while(~scanf("%c%d %c%d", &m1, &n1, &m2, &n2))
{
getchar(); //注意还有一个换行符!
memset(visit, 0, sizeof(visit));
state start, target;
start.row = (int)(m1 - 'a');
start.col = n1 - 1;
start.steps = 0;
target.row = (int)(m2 - 'a');
target.col = n2 - 1;
visit[start.row][start.col] = 1;
//cout << "DEFAULT:" << start.row << start.col << " " << target.row << target.col << endl;
state cur, next;
cur = start;
queue<state> que;
que.push(cur);
while(!que.empty())
{
cur = que.front();
//cout << "DEFAULT(father):" << cur.row+1 << cur.col+1 << endl;
que.pop();
if(cur.row == target.row && cur.col == target.col)
{
printf("To get from %c%d to %c%d takes %d knight moves.\n", m1, n1, m2, n2, cur.steps);
break;
}
for(int i = 0; i < 8; i++)
{
next.row = cur.row + moves[i][0];
next.col = cur.col + moves[i][1];
if((next.row < 8) && (next.col < 8) && (next.row >= 0) && (next.col >= 0) && (visit[next.row][next.col] == 0))
{
next.steps = cur.steps + 1;
visit[next.row][next.col] = 1;
que.push(next);
//defaul++;
//cout << "DEFAULT:" << next.row+1 << next.col+1 << " " << next.steps << endl;
}
}
}
//cout << "DEFAULT:" << defaul << endl;
}
return 0;
}