您的位置:首页 > 其它

week2作业题_bfs迷宫问题&bfs倒水问题

2020-03-10 01:11 134 查看

A-maze

题目描述:

东东有一张地图,想通过地图找到妹纸。地图显示,0表示可以走,1表示不可以走,左上角是入口,右下角是妹纸,这两个位置保证为0。既然已经知道了地图,那么东东找到妹纸就不难了,请你编一个程序,写出东东找到妹纸的最短路线。

Input: 输入是一个5 × 5的二维数组,仅由0、1两数字组成,表示法阵地图。
Output:输出若干行,表示从左上角到右下角的最短路径依次经过的坐标,格式如样例所示。数据保证有唯一解。

Sample Input:
0 1 0 0 0
0 1 0 1 0
0 1 0 1 0
0 0 0 1 0
0 1 0 1 0
Sample Output:
(0, 0)
(1, 0)
(2, 0)
(3, 0)
(3, 1)
(3, 2)
(2, 2)
(1, 2)
(0, 2)
(0, 3)
(0, 4)
(1, 4)
(2, 4)
(3, 4)
(4,4)
Hint:
坐标(x, y)表示第x行第y列,行、列的编号从0开始,且以左上角为原点。 另外注意,输出中分隔坐标的逗号后面应当有一个空格。

题目思路:

在该题目中可以使用bfs从起点搜索,如果能够搜索到终点就说明存在起点到终点的路径。在使用bfs搜索的时候,我们需要找到现在搜索到的点的所有未到达的邻接点。在迷宫中,它的邻接点就是上下左右的最多四个点,如果邻接点存在并且未到达(即为0)则将该邻接点加入队列,并将它相应的位置置为1,标记为已到达。为了方边查找上下左右的邻接点,定义了坐标变化的dx[]和dy[]两个数组。

const int dx[] = {0 , 0 , -1 , 1}; //上下左右
const int dy[] = {-1 , 1 , 0 , 0}; //上下左右

为了能够确定起点到终点的路径,我将每个点的坐标和它的前一个到达它的点的坐标用一个结构体对象存储,即定义了结构体类型Path。这样我们就可以确定路径。

struct Path{  //用来记录路径
int x , y;
int px , py; //前驱坐标
}

最后我们可以使用递归的方法输出路径(当然也可以使用迭代的方法)。

代码实现:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;

struct Path{		//用来记录路径
int x , y;
int px , py;	//前驱坐标
}path[5][5];

int maze[5][5];		//记录迷宫
const int dx[] = {0 , 0 , -1 , 1};	//上下左右
const int dy[] = {-1 , 1 , 0 , 0};	//上下左右

void bfs(int a = 0, int b = 0)
{
queue<Path> p;
maze[a][b] = 1;
path[a][b].x = path[a][b].y = 0;
path[a][b].px = path[a][b].py = -1;
p.push(path[a][b]);
while(!p.empty())
{
Path f = p.front();
p.pop();

if(f.x == 4 && f.y == 4)	//到达终点
return ;
int x1 , y1;
for(int i = 0; i < 4; i++)
{
x1 = f.x + dx[i];	//f点的邻接点
y1 = f.y + dy[i];
if(x1 >= 0 && x1 < 5 && y1 >= 0 && y1 <5)		//判断是否有效
{
if(maze[x1][y1] == 0)			//未到达且没有障碍物
{
maze[x1][y1] = 1;
path[x1][y1].x = x1;
path[x1][y1].y = y1;
path[x1][y1].px = f.x;
path[x1][y1].py = f.y;
p.push(path[x1][y1]);
}
}
}
}
}

void findpath(int x, int y)	//输出路径
{
if(x == -1 && y == -1) return ;
findpath(path[x][y].px , path[x][y].py);
cout << "(" << x << ", " << y << ")" << endl;
}

int main()
{
for(int i = 0; i < 5; i++)
{
for(int j = 0; j < 5; j++)
{
cin >> maze[i][j];
}
}
bfs();
findpath(4,4);

}

B-pour water

题目描述

倒水问题"fill A" 表示倒满A杯,"empty A"表示倒空A杯,“pour A B” 表示把A的水倒到B杯并且把B杯倒满或A倒空。

Input:输入包含多组数据。每组数据输入 A, B, C 数据范围 0 < A <= B 、C <= B <=1000 、A和B互质。
Output:你的程序的输出将由一系列的指令组成。这些输出行将导致任何一个罐子正好包含C单位的水。每组数据的最后一行输出应该是“success”。输出行从第1列开始,不应该有空行或任何尾随空格。

Sample Input:
2 7 5
2 7 4
Sample Output:
fill B
pour B A
success
fill A
pour A B
fill A
pour A B
success

Notes:
如果你的输出与Sample Output不同,那没关系。对于某个"A B C"本题的答案是多解的,不能通过标准的文本对比来判定你程序的正确与否。
所以本题由 SPJ(Special Judge)程序来判定你写的代码是否正确。

题目思路

在这个题目中可以把A,B两个水杯的状态即水的量看作是隐示图。从某一个状态经过一次倒水,得到的另一个状态就是该状态的“邻接点”。而倒水的情况总共分为六种情况:倒满A,倒满B,倒空A,倒空B,将A到入B中,将B倒入A中。每种状态的“邻接点”都是从该状态经过上面六个变换中的一种得到的。在数据存储的时候使用了STATUS和PSTATUS两种结构体类型,分别表示现在的状态和倒水的前一个状态和倒水的方式。然后使用map存储倒水的过程。

const string fA = "fill A";		//倒满A
const string eA = "empty A";	//倒空A
const string pAB = "pour A B";	//将A到入B中
const string fB = "fill B";		//倒满B
const string eB = "empty B";	//倒空B
const string pBA = "pour B A";	//将B倒入A中

struct STATUS{
int a , b;	//现在水杯的状态
}

struct PSTATUS{
int pa , pb;	//上一次操作前水杯的状态
string turn;	//从上一个状态到现在状态的操作
}

map<STATUS , PSTATUS> mp;		//倒水的过程

从初始状态也就是A = 0,B = 0开始,然后使用bfs搜索,将每个可到达的并且未到达的状态加入队列中,一直到A = C或者B = C或者队列为空结束。结果输出是使用递归实现的。

代码实现:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <map>
using namespace std;

const string fA = "fill A";		//倒满A
const string eA = "empty A";	//倒空A
const string pAB = "pour A B";	//将A到入B中
const string fB = "fill B";		//倒满B
const string eB = "empty B";	//倒空B
const string pBA = "pour B A";	//将B倒入A中

struct STATUS{
int a , b;	//现在水杯的状态

bool operator < (const STATUS &y)const
{
return a != y.a ? a < y.a : b < y.b;
}
};

struct PSTATUS{
int pa , pb;	//上一次操作前水杯的状态
string turn;	//从上一个状态到现在状态的操作

bool operator < (const PSTATUS &y)const
{
return pa != y.pa ? pa < y.pa : pb < y.pb;
}
};

queue<STATUS> Q;
map<STATUS , PSTATUS> mp;		//倒水的过程

STATUS bfs(int A , int B , int C)
{
STATUS s;
PSTATUS ps;
s.a = s.b = 0;
ps.pa = ps.pb = -1;
mp[s] = ps;

Q.push(s);

while(!Q.empty())
{
s = Q.front();
Q.pop();

STATUS t;
PSTATUS pt;

if(s.a == C || s.b == C) 	//结束
{
return s;
}

if(s.a < A)	//倒满a
{
t.a = A;
t.b = s.b;
pt.pa = s.a;
pt.pb = s.b;
pt.turn = fA;
if(mp.find(t) == mp.end())
{
mp[t] = pt;
Q.push(t);
}
}
if(s.b < B)	//倒满b
{
t.a = s.a;
t.b = B;
pt.pa = s.a;
pt.pb = s.b;
pt.turn = fB;
if(mp.find(t) == mp.end())
{
mp[t] = pt;
Q.push(t);
}
}
if(s.a > 0)	//倒空A
{
t.a = 0;
t.b = s.b;
pt.pa = s.a;
pt.pb = s.b;
pt.turn = eA;
if(mp.find(t) == mp.end())
{
mp[t] = pt;
Q.push(t);
}
}
if(s.b > 0)	//倒空B
{
t.a = s.a;
t.b = 0;
pt.pa = s.a;
pt.pb = s.b;
pt.turn = eB;
if(mp.find(t) == mp.end())
{
mp[t] = pt;
Q.push(t);
}
}
if(s.b > 0 && s.a < A)	//将B倒入A中
{
pt.pa = s.a;
pt.pb = s.b;
pt.turn = pBA;
if(s.a + s.b >= A)	//把A倒满
{
t.a = A;
t.b = s.a + s.b - A;
if(mp.find(t) == mp.end())
{
mp[t] = pt;
Q.push(t);
}
}
else{			//A没有倒满
t.a = s.a + s.b;
t.b = 0;
if(mp.find(t) == mp.end())
{
mp[t] = pt;
Q.push(t);
}
}
}
if(s.a > 0 && s.b < B)	//将A倒入B中
{
pt.pa = s.a;
pt.pb = s.b;
pt.turn = pAB;
if(s.a + s.b >= B)		//把B倒满
{
t.a = s.a + s.b - B;
t.b = B;
if(mp.find(t) == mp.end())
{
mp[t] = pt;
Q.push(t);
}
}
else{				//B没有倒满
t.a = 0;
t.b = s.a + s.b;
if(mp.find(t) == mp.end())
{
mp[t] = pt;
Q.push(t);
}
}
}
}

return s;
}

void print(STATUS &s)	//输出结果
{
if(s.a == 0 && s.b == 0)
return ;
STATUS ps;
ps.a = mp[s].pa;
ps.b = mp[s].pb;
print(ps);
cout << mp[s].turn << endl;
}

int main()
{
int A, B, C;
while(cin >> A >> B >> C)
{
while(!Q.empty())		//清空队列
Q.pop();
STATUS ans = bfs(A,B,C);
print(ans);
cout << "success" <<endl;
for(map<STATUS,PSTATUS>::iterator i = mp.begin();i != mp.end();)	//清空map
mp. erase(i++);
}
}

总结

  1. map的初始化是在c++11之后的版本才有的,之前的版本是没有map的初始化的。
  2. 由于实验中queue Q和map mp都是全局的,在每次开始一次的时候都要将其清空,否则就会出现之前的数据对后面的结果产生影响。
  • 点赞 2
  • 收藏
  • 分享
  • 文章举报
小芳× 发布了2 篇原创文章 · 获赞 2 · 访问量 60 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: