实验一:迷宫问题
问题描述:
东东有一张地图,想通过地图找到妹纸。地图显示,0表示可以走,1表示不可以走,左上角是入口,右下角是妹纸,这两个位置保证为0。既然已经知道了地图,那么东东找到妹纸就不难了,请你编一个程序,写出东东找到妹纸的最短路线。
input:
输入是一个5 × 5的二维数组,仅由0、1两数字组成,表示法阵地图
output:
输出若干行,表示从左上角到右下角的最短路径依次经过的坐标,格式如样例所示。数据保证有唯一解。
样例输入:
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
样例输出:
(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)
思考:
题目要求为最短路径,最短路径的我们常用的方法为kruskal,prim,bfs
从题目中分析该题采用bfs广度优先搜索是最常用的方法
设置起始位置和终点位置,每一次走有上下左右四种选择,边界判断条件为位置x,y属于0到4之间(包括边界)
用-2来表示该格子未访问过,-1表示障碍物,起始点置为0,每次根据上一个节点离初始点的距离记录着一个结点的距初始点的距离,并将前序节点保存在pre数组中,代码如下:
void BFS(int sx,int sy)
{
queue<Pair> a;
a.push({sx,sy});
//初始点置为0
vis[sx][sy] = 0;
while(!a.empty())
{
Pair nowPair = a.front() ;
a.pop();
for(int i = 0;i < 4;i++)
{
Pair newPair = nowPair;
newPair.x = nowPair.x + gx[i];
newPair.y = nowPair.y + gy[i];
if(newPair.x<=4&&newPair.x>=0&&newPair.y<=4&&newPair.y>=0)
{
if(vis[newPair.x][newPair.y] == -2)
{
//根据前序节点距离初始点的位置记录现在节点距离初始点的位置
vis[newPair.x][newPair.y] = vis[nowPair.x][nowPair.y] + 1;
//记录前序节点
pre[newPair.x][newPair.y] = nowPair.x*10+nowPair.y;
a.push(newPair);
}
if(newPair.x==4&&newPair.y==4)
{
return;
}
}
}
}
}
最后为递归实现的输出路径的代码:
pre数组首先全初始化为-1,之后再将前序节点的x值乘10加上有y值保存在数组之中:
void print(int x,int y)
{
if(pre[x][y]!=-1)
{
print(pre[x][y]/10,pre[x][y]%10);
}
if(x==4&&y==4)
{
cout<<"("<<x<<", "<<y<<")";
return;
}
cout<<"("<<x<<", "<<y<<")"<<endl;
}
**
实验二:倒水问题
**
问题描述:
倒水问题
“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列开始,不应该有空行或任何尾随空格
simple input:
2 7 5
2 7 4
simple output:
fill B
pour B A
success
fill A
pour A B
fill A
pour A B
success
解题方法1:BFS广度优先搜索
思路:类似于上题思路,需要两个矩阵记录水量和路径
因为题目给出的范围我们将两个矩阵的范围设置成1005*1005
每一次操作有六种可能性:
倒满A杯
倒满B杯
倒空A杯
倒空B杯
B杯的水倒入A杯
A杯的水倒入B杯
我们在vis矩阵中记录前一个状态到达这一个状态时需要进行的操作分别用1,2,3,4,5,6表示,没有到达的位置用-1表示。
void print(int i)
{
switch(i){
case 1:
cout<<"fill A"<<endl;break;
case 2:
cout<<"fill B"<<endl;break;
case 3:
cout<<"empty A"<<endl;break;
case 4:
cout<<"empty B"<<endl;break;
case 5:
cout<<"pour A B"<<endl;break;
case 6:
cout<<"pour B A"<<endl;break;
将六种情况每次都加入对列判断是否走过,当最终结果等于C时,根据vis和pre输出步骤,这个方法求出来的是最短的到达终点的路径
int BFS()
{
queue<Node> q;
memset(vis,0,sizeof(vis));
memset(pre,-1,sizeof(pre));
Node node;
node.a = 0;node.b = 0;
vis[node.a][node.a] = 1;
q.push(node);
while(!q.empty())
{
Node t = q.front(),p;
q.pop();
if(t.a==c||t.b==c)
{
path(t.a,t.b);
return 1;
}
if(!vis[a][t.b])//fill A
{
p.a = a;
p.b = t.b;
vis[p.a][p.b] = 1;
pre[p.a][p.b] = t.a*1001+t.b;//记录前序,因为范围是0~1000所以开1001,让尾数能表示0~1000记录在pre前序里
q.push(p);
}
if(!vis[t.a][b])//fill B
{
p.a = t.a;
p.b = b;
vis[p.a][p.b] = 2;
pre[p.a][p.b] = t.a*1001+t.b;
q.push(p);
}
if(!vis[0][t.b])//drop A
{
p.a = 0;
p.b = t.b;
vis[p.a][p.b] = 3;
pre[p.a][p.b] = t.a*1001+t.b;
q.push(p);
}
if(!vis[t.a][0])//drop B
{
p.a = t.a;
p.b = 0;
vis[p.a][p.b] = 4;
pre[p.a][p.b] = t.a*1001+t.b;
q.push(p);
}
if(!vis[(a-t.a>=t.b)?t.a+t.b:a][(a-t.a>=t.b)?0:t.b-a+t.a])//pour B A
{
p.a = (a-t.a>=t.b)?t.a+t.b:a;
p.b = (a-t.a>=t.b)?0:t.b-(a-t.a);
vis[p.a][p.b] = 6;
pre[p.a][p.b] = t.a*1001+t.b;
q.push(p);
}
if(!vis[(b-t.b>=t.a)?0:t.a-b+t.b][(b-t.b>=t.a)?t.a+t.b:b])
{
p.a = (b-t.b>=t.a)?0:t.a-(b-t.b);
p.b = (b-t.b>=t.a)?t.a+t.b:b;
vis[p.a][p.b] = 5;
pre[p.a][p.b] = t.a*1001+t.b;
q.push(p);
}
}
return 0;
}
方法二:
都题干,我们发现AB两个杯子的容量是互质的,那么根据数学上的定理,
n*A/B的值在1到B-1之间循环,那么根据C小于B这个前提条件,一定会有一种状态使B中水为C
那么我们模拟除法过程
A中倒满水,倒入B中
此时有两种情况第一种是B中还能到下A杯中的水,则倒入判断B中水量
第二种则是,B中不能倒下A中的水,则A中的水倒满B,B倒光,在将A中剩余的水倒入B,判断,反复循环知道找到C
```cpp
void daoshui()
{
int A,B;
while(1)
{
cout<<"fill A"<<endl;
A = a;
if(b - B < A)
{
A = A - (b - B);
cout<<"pour A B"<<endl;
B = 0;
cout<<"empty B"<<endl;
B = A;
cout<<"pour A B"<<endl;
}else if (b - B >= A)
{
B = A + B;
A = 0;
cout<<"pour A B"<<endl;
}
if(B == c)
{
cout<<"success"<<endl;
return;
}
}
}