预备知识
队列
特点:
1、先进先出(FIFO)
2、从队头删除元素
3、在队尾加入元素
常见操作
1)判断队列是否为空
2)查询队列大小
3)访问队首元素
4)访问队尾元素
5)加入元素
6)删除元素
STL中队列的基本用法
1)创建队列对象:queue<元素类型>队列名;
2)队列添加元素:队列名.push(元素名);
3)去掉队首元素:队列名.pop();
4)访问队首元素:队列名.front();
5)访问队尾元素:队列名.back();
6)判断是否为空:队列名.empty();
7)返回队列大小:队列名.size();
现在来看BFS,首先看一个例题。
二叉树的层次遍历。
很显然层次遍历的结果为:5 1 7 2 4 6 3.
那么我们如何通过代码实现呢?
算法思想:
维护一个队列,用于存放结点的信息。当访问到一个结点的时候,先访问该结点,然后将该结点的左右儿子分别入队列。
伪代码:
ccbl(int root)
queue<int>Q;创建一个队列
Q.push(root);将根结点入队列
while(队列不为空){
获得队首元素
将队首元素出队
输出当前结点的值
如果该结点的左儿子不为空,将左儿子加入到队列中
如果该结点的右儿子不为空,将右儿子加入到队列中
}
图的BFS示意图
我们再看一个例题:
题目链接:Problem - 1548 (hdu.edu.cn)
问题描述:
Sample Input
5 1 5
3 3 1 2 5
0
Sample Output
3
解题代码
#include<bits/stdc++.h>
using namespace std;
int N,Start,End;
int a[202];
int vis[202]; // 标记数组
struct pos
{
int level;
int steps;
};
void bfs(); // 函数原型声明
int main()
{
while(scanf("%d",&N)==1)
{
if(N==0)
break;
scanf("%d%d",&Start,&End);
for(int i=1;i<=N;i++)
{
scanf("%d",&a[i]);
vis[i]=0;
}
bfs();
}
return 0;
}
void bfs()
{
pos cur,nex;
cur.level=Start;
cur.steps=0;
queue<pos>qu;
qu.push(cur);
vis[Start]=1;
while(!qu.empty())
{
cur=qu.front();
qu.pop();
if(cur.level == End)
{
printf("%d\n",cur.steps);
return;
}
nex.level=cur.level+a[cur.level];
nex.steps=cur.steps+1;
if(nex.level <= N)
{
if(vis[nex.level]==0)
{
vis[nex.level]=1;
qu.push(nex);
}
}
nex.level=cur.level-a[cur.level];
nex.steps=cur.steps+1;
if(nex.level>=1)
{
if(vis[nex.level]==0)
{
vis[nex.level]=1;
qu.push(nex);
}
}
}
printf("-1\n");
return;
}
运行结果
下面我们再来看一道例题:
题目链接:Problem - 1495 (hdu.edu.cn)
Problem Description
大家一定觉的运动以后喝可乐是一件很惬意的事情,但是seeyou却不这么认为。因为每次当seeyou买了可乐以后,阿牛就要求和seeyou一起分享这一瓶可乐,而且一定要喝的和seeyou一样多。但seeyou的手中只有两个杯子,它们的容量分别是N 毫升和M 毫升 可乐的体积为S (S<101)毫升 (正好装满一瓶) ,它们三个之间可以相互倒可乐 (都是没有刻度的,且 S==N+M,101>S>0,N>0,M>0) 。聪明的ACMER你们说他们能平分吗?如果能请输出倒可乐的最少的次数,如果不能输出"NO"。
Input
三个整数 : S 可乐的体积 , N 和 M是两个杯子的容量,以"0 0 0"结束。
Output
如果能平分的话请输出最少要倒的次数,否则输出"NO"。
Sample Input
7 4 3
4 1 3
0 0 0
Sample Output
NO
3
Author
seeyou
Source
“2006校园文化活动月”之“校庆杯”大学生程序设计竞赛暨杭州电子科技大学第四届大学生程序设计竞赛
解题代码
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
int s,n,m;
int vis[105][105][105];
struct node{
int s,n,m;
int step;
};
bool check(int x,int y,int z){
return x == 0 && y == z || y == 0 && x == z || z == 0 && x == y;
}
int bfs(){
if(s % 2) return 0;
queue<node> q;
node now,next;
now.s = s,now.n = 0,now.m = 0,now.step = 0;
q.push(now);
vis[s][0][0] = 1;
while(q.size()){
now = q.front();
q.pop();
if(check(now.s,now.n,now.m)){
return now.step;
}
// s-->n
if(now.s && now.n < n){ // s不为空且n未满
if(now.s < n - now.n){ //s无法倒满n
next.n = now.n + now.s;
next.s = 0;
next.m = now.m;
}
else{ //s可以倒满 n
next.s = now.s - (n - now.n);
next.n = n;
next.m = now.m;
}
if(!vis[next.s][next.n][next.m]){
next.step = now.step + 1;
vis[next.s][next.n][next.m] = 1;
q.push(next);
}
}
// s-->m
if(now.s && now.m < m){ // s不为空且m未满
if(now.s < m - now.m){ //s无法倒满m
next.m = now.m + now.s;
next.s = 0;
next.n = now.n;
}
else{ //s可以倒满 m
next.s = now.s - (m - now.m);
next.m = m;
next.n = now.n;
}
if(!vis[next.s][next.n][next.m]){
next.step = now.step + 1;
q.push(next);
vis[next.s][next.n][next.m] = 1;
}
}
// n-->s
if(now.n && now.s < s){ // n不为空且s未满
if(now.n < s - now.s){ //n无法倒满s
next.s = now.s + now.n;
next.n = 0;
next.m = now.m;
}
else{ //n可以倒满 s
next.n = now.n - (s - now.s);
next.s = s;
next.m = now.m;
}
if(!vis[next.s][next.n][next.m]){
next.step = now.step + 1;
q.push(next);
vis[next.s][next.n][next.m] = 1;
}
}
// n-->m
if(now.n && now.m < m){ // n不为空且m未满
if(now.n < m - now.m){ //n无法倒满m
next.m = now.m + now.n;
next.n = 0;
next.s = now.s;
}
else{ //n可以倒满 m
next.n = now.n - (m - now.m);
next.m = m;
next.s = now.s;
}
if(!vis[next.s][next.n][next.m]){
next.step = now.step + 1;
q.push(next);
vis[next.s][next.n][next.m] = 1;
}
}
// m-->s
if(now.m && now.s < s){ // m不为空且s未满
if(now.m < s - now.s){ //m无法倒满s
next.s = now.s + now.m;
next.m = 0;
next.n = now.n;
}
else{ //m可以倒满 s
next.m = now.m - (s - now.s);
next.s = s;
next.n = now.n;
}
if(!vis[next.s][next.n][next.m]){
next.step = now.step + 1;
q.push(next);
vis[next.s][next.n][next.m] = 1;
}
}
// m-->n
if(now.m && now.n < n){ // m不为空且n未满
if(now.m < n - now.n){ //m无法倒满n
next.n = now.n + now.m;
next.m = 0;
next.s = now.s;
}
else{ //m可以倒满 n
next.m = now.m - (n - now.n);
next.n = n;
next.s = now.s;
}
if(!vis[next.s][next.n][next.m]){
next.step = now.step + 1;
q.push(next);
vis[next.s][next.n][next.m] = 1;
}
}
}
return 0;
}
int main()
{
while(cin>>s>>n>>m && s && n && m){
memset(vis,0,sizeof(vis));
int ans = bfs();
if(ans == 0)
cout<<"NO"<<endl;
else
cout<<ans<<endl;
}
return 0;
}
运行结果
状态转移规则(倒水规则):
如果i水杯内水的容量大于j水杯内倒满所需的容量x,则——i水杯倒水后的容量为:i-x,j水杯倒水后容量为:j+x.
如果i水杯内水的容量小于j水杯内倒满所需的容量x,则——i水杯倒水后的容量为:0,j水杯倒水后容量为:j+x.
每操作一次,最少倒水次数+1.
总结
BFS基本思想:
从初始状态S开始,利用规则,生成所有可能的状态,构成树的下一层结点。
检查是否出现目标状态G,若未出现,就对该层所有状态结点,分别依次利用规则,生成再下一层的所有状态结点。
对新一层的所有状态结点继续检查是否出现G,若未出现,继续按上面思想生成再下一层的所有状态结点,这样一层一层往下展开,直到出现目标状态为止。
BFS算法(伪代码):
Node bfs(node source,node target){
memset(visit,0,sizeof(visit));
queue<node>Q;
Q.push(source);
visit[source]=1;
while(!Q.empty()){
Node a = Q.front();
Q.pop();
if(a==target)
{ return a; }
for(对于a所有的后继结点b)
if(visit[b]){ continue; }
Q.push(b);
visit[b]=1;// 剪枝,保证结点只进队列一次
}
return NULL;
}