程序设计作业week02

A - Maze

题目
在这里插入图片描述样例

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)

在这里插入图片描述
解题思路

这是一个经典的迷宫题(四连通),搜索路径问题可以用dfs也可以用bfs。这道题目的要求是求出找妹纸的最短路径,bfs一层一层地搜索最先找到终点的路径就是最短的,如果用dfs的话要找到所有解进行选择,所以我在这里采用bfs的方法。

定义mp数组存地图,vis数组用于判断结点是否被访问过,初始化为全0表示最开始没有被访问过。定义结构体node存储节点的坐标信息,大小也是5*5.然后定义一个node类型的队列,在bfs中记录节点。

int mp[5][5];
int vis[5][5];
struct node
{
	int x, y;
};
node pre[5][5];
queue<node> q;

广度优先搜索bfs()
这里就是普通的广度优先搜索

先将起点(0,0)入队列,并标记,即vis[0][0]=1.
然后进入循环,取出队首并记为p,然后pop该节点

搜索该节点的四个可到达的节点,这里搜索采用循环的方式,让坐标加上-1或0或1.因为只有上下左右四个方向,所以要增加一个约束,x、y的增加量之和要么是1,要么是-1.这样就可以剔除斜着四个方向和原地不动的情况

遍历这四个节点时,要判断是否为有效点,判断是否坐标越出地图范围,以及该点是否被访问过,即vis值是否为1.

如果是可以访问的节点,就加入队列中,并标记vis为1,用pre记录新加入节点的前一个节点(相当于爸爸qwq),以便于后面输出路径


```cpp
for(int i=-1;i<2;i++)
		{
			for(int j=-1;j<2;j++)
			{
				node nn;
				nn.x = p.x+i;
				nn.y = p.y+j;
				if(i+j==1||i+j==-1)
				{
					if((nn.x<0||nn.x>4||nn.y<0||nn.y>4)||vis[nn.x][nn.y]==1||mp[nn.x][nn.y]==1)
					continue;
					q.push(nn);
					vis[nn.x][nn.y] = 1;
					pre[nn.x][nn.y] = p;
				}
			}
		}

循环直至队列为空(其实想到这个题目里在循环末尾可以加一个判断,如果找到终点(4,4)就停止搜索清空队列来着。。。因为第一次遇见终点就是最短路径了qwq写的时候没多想)

输出路径track()

因为在广搜时记录了节点的前一个节点,在输出路径时通常有两种方法:一是从终点往前推,把一路上的节点都存在一个数组里,最后逆序输出;另一个就是递归方法,从终点开始不停递归上一个节点,直到递归函数的参数是起点,返回上一层输出。两种方法都可,在这里我用的是递归。


```cpp
void track(node pp)
{
	if(pp.x==0&&pp.y==0)
	{
		cout<<"(0, 0)"<<endl;
		return;
	}
	track(pre[pp.x][pp.y]);
	cout<<'('<<pp.x<<", "<<pp.y<<')'<<endl;
}

完整代码


```cpp
#include <iostream>
#include <queue>
using namespace std;

int mp[5][5];
int vis[5][5];
struct node
{
	int x, y;
};
node pre[5][5];
queue<node> q;

void bfs()
{
	node p;
	p.x = 0;	p.y = 0;
	q.push(p);	
	vis[0][0] = 1;
	while(!q.empty())
	{
		p = q.front();
		q.pop();
		for(int i=-1;i<2;i++)
		{
			for(int j=-1;j<2;j++)
			{
				node nn;
				nn.x = p.x+i;
				nn.y = p.y+j;
				if(i+j==1||i+j==-1)
				{
					if((nn.x<0||nn.x>4||nn.y<0||nn.y>4)||vis[nn.x][nn.y]==1||mp[nn.x][nn.y]==1)
					continue;
					q.push(nn);
					vis[nn.x][nn.y] = 1;
					pre[nn.x][nn.y] = p;
				}
			}
		}
	}
}
void track(node pp)
{
	if(pp.x==0&&pp.y==0)
	{
		cout<<"(0, 0)"<<endl;
		return;
	}
	track(pre[pp.x][pp.y]);
	cout<<'('<<pp.x<<", "<<pp.y<<')'<<endl;
}
int main()
{
	for(int i=0;i<5;i++)
	{
		for(int j=0;j<5;j++)
		{
			cin>>mp[i][j];
			vis[i][j] = 0;
		}
	}
	bfs();
	node end;
	end.x = 4;	end.y = 4;
	track(end);
	return 0;
}

B - Pour Water

题目
在这里插入图片描述在这里插入图片描述样例

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

解题思路
倒水问题是一个隐式图问题。在这里插入图片描述
这里的初始节点(状态)就是A、B两个杯子里面的水都是0,目标节点即要达到的状态,中间的状态是未知的,搜索过程中要按照规则扩展节点。

倒水问题的规则里有六种操作,分别是:
fill A
fill B
empty A
empty B
pour A B(把A中的水倒入B中)
pour B A
以上所述的六种情况中的前四种比较简单,fill只要把A或者B的值赋满就行,empty则直接赋0。后面两种情况需要进行讨论,这里以pour A B为例(pour B A同理)

pour A B可能会出现两种情况:一是A中的水还没倒完,B就满了,此时A中还有剩余;另一种是在B没有溢出的情况下,A中的水全部倒完了,此时B不一定满,而A一定为空。

状态分析完后,开始实现bfs

同样的,定义一个数组vis来标记状态是否被访问过,结构体node用来表示状态结点,x、y分别表示A、B杯中有多少水。

为方便后面输出操作过程,在这里我定义了一个结构体,存上一个结点的状态和该节点是由哪一种操作得来的(嗯,六个操作用0-5表示,要输出的字符串也以相应的下标存在一个string类数组里)

定义一个结构体数组nw[6],表示当前节点通过六种操作扩展得到的六种状态。


```cpp
for(int i=0;i<6;i++)
			nw[i]=pre;
		nw[0].x=n;  
		nw[1].x=0;  
		nw[3].y=m; 
		nw[4].y=0;  
		if (nw[2].x>=m-nw[2].y)  
			{
				nw[2].x-=(m-nw[2].y);
				nw[2].y=m;
			}
		else
			{
				nw[2].y+=nw[2].x;
				nw[2].x=0;
			}
		if (nw[5].y>=n-nw[5].x)  
			{
				nw[5].y-=(n-nw[5].x);
				nw[5].x=n;
			}
		else
			{
				nw[5].x+=nw[5].y;
				nw[5].y=0;
			}

通过标记数组vis判断状态是否被搜索过,选择没有访问过得状态加入队列,并打上标记……在循环对六种状态进行判断时,可以同时判断该节点是否是目标状态,若是,直接返回目标节点。

for(int i=0;i<6;i++)
		{
			if (vis[nw[i].x][nw[i].y]==0)
			{
				vis[nw[i].x][nw[i].y]=1;
				q.push(nw[i]);
				r[nw[i].x][nw[i].y].p=pre;
				r[nw[i].x][nw[i].y].rote=i;
			}
			if (nw[i].x==d || nw[i].y==d)
				return nw[i];
		}

除了这些细节处理的问题,其他地方跟A题所描述的bfs过程差不多,入队、出队、递归等。

完整代码

#include <iostream>
#include <queue>
#include <cstring>
#include <string>
using namespace std;

string s[6]={"fill A","empty A","pour A B", "fill B","empty B","pour B A"};
int vis[1010][1010];
int n,m,d;
 
struct node
{
	int x,y;
};
struct track
{
	int rote;
	node p;
}r[1010][1010];
 
node bfs()
{
	memset(vis,0,sizeof(vis));
	for(int i=0;i<1010;i++)
	{
		for(int j=0;j<1010;j++)
		{
			r[i][j].rote=-1;
			r[i][j].p.x=-1;
			r[i][j].p.y=-1;
		}
	}
	node t,nw[6];
	t.x=t.y=0;
	queue<node> q;
	q.push(t);
	vis[t.x][t.y]=1;
	while(!q.empty())
	{
		node pre=q.front();
		q.pop();
		for(int i=0;i<6;i++)
			nw[i]=pre;
		nw[0].x=n;  
		nw[1].x=0;  
		nw[3].y=m; 
		nw[4].y=0;  
		if (nw[2].x>=m-nw[2].y)  
			{
				nw[2].x-=(m-nw[2].y);
				nw[2].y=m;
			}
		else
			{
				nw[2].y+=nw[2].x;
				nw[2].x=0;
			}
		if (nw[5].y>=n-nw[5].x)  
			{
				nw[5].y-=(n-nw[5].x);
				nw[5].x=n;
			}
		else
			{
				nw[5].x+=nw[5].y;
				nw[5].y=0;
			}
		for(int i=0;i<6;i++)
		{
			if (vis[nw[i].x][nw[i].y]==0)
			{
				vis[nw[i].x][nw[i].y]=1;
				q.push(nw[i]);
				r[nw[i].x][nw[i].y].p=pre;
				r[nw[i].x][nw[i].y].rote=i;
			}
			if (nw[i].x==d || nw[i].y==d)
				return nw[i];
		}
	}
}
 
void Print(node pp)
{
	if(pp.x==0 && pp.y==0)
		return;
	Print(r[pp.x][pp.y].p);
	cout<<s[r[pp.x][pp.y].rote]<<endl;
	return;
}
 
int main ()
{
	while(cin>>n>>m>>d)
	{
		Print(bfs());
		cout<<"success"<<endl;
	}
	return 0;
}

关于B题的一个小事情(嗷)
emmm……做这题没少花功夫……为什么呢hhhhh,这题在提交的时候用g++评测结果是RE,对RE的结果我还深信不疑肯定是我哪里写错了qwq找了很久很久……后来觉得不可能有错误w我就去试了c++评测,结果直接给CE了(窝爆哭www)。
嗯,最后还是发现了一个代码的问题,在头文件那。用string类习惯性的写cstring头文件,加上string头文件后就AC了。才知道有区别hhh查了一波也是长知识了。
这个提交找问题的过程花了接近两个小时(还是wtcl嘿嘿),发现问题就好qwq

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值