【20200226程序设计思维与实践 Week2 作业】Maze & Pour Water

这篇博客介绍了两个编程问题的解决思路:一是使用广度优先搜索解决迷宫问题,二是利用隐式图搜索解决倒水问题。在迷宫问题中,通过设计迷宫单元结构体和广度优先搜索算法找到最短路线。倒水问题通过转化成隐式图,运用类似的方法寻找满足条件的操作序列。博主总结了在应用广度优先搜索时的技巧和注意事项。
摘要由CSDN通过智能技术生成


A - Maze

题意

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


思路:

标准的广度优先搜索。

设计了迷宫单元结构体unit,包含成员数据坐标xy,父坐标pxpy,不可通行标记block,已到达标记reach;重载了赋值运算符。

准备了方向数组dxdy,开辟了全局二维unit数组maze。为了便于程序识别迷宫边界,将迷宫起点平移至(1,1),代码内始终如此偏移,主函数中将迷宫边界的block全部置1。

设计了广度优先搜索bfs(),借助队列实现。循环在队列清空前始终继续,每次取出队列头检查四个方向并将未抵达的可通行方向加入队列,并更新该方向的父节点,直到到达终点。

设计了寻路函数way(),借助栈实现,bfs()运行完后即从终点开始回溯父节点,不断将父节点压入栈直到到达起点。

输出函数从栈中取出走过的路径坐标,恢复偏移后按格式输出。


总结:

巩固了广度优先搜索的知识,练习了bfs的应用及代码书写中的一些技巧,如建立方向数组以精简代码等。


代码:

#include<iostream>
#include<stack>
#include<queue>
using namespace std;

struct unit{//地图单元 
	int x,y;//坐标 
	int px,py;//父坐标 
	bool block;//不可通行标记 
	bool reach;//已到达标记 
	
	void operator=(unit& u){
		x=u.x;
		y=u.y;
		px=u.px;
		py=u.py;
		block=u.block;
		reach=u.reach;
	}
};

//方向数组 
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};

unit maze[8][8];
//为便于程序书写 代码内将迷宫起点平移至(1,1) 

queue<unit> Q;

void bfs(){
	Q.push(maze[1][1]);
	while(!Q.empty()){//队列清空前始终循环 
		unit thisOne=Q.front();
		Q.pop();
		int x=thisOne.x;
		int y=thisOne.y;
		for(int i=0;i<4;i++){//检查四个方向并入队 
			if(!maze[x+dx[i]][y+dy[i]].block){
			    maze[x+dx[i]][y+dy[i]].px=x;
				maze[x+dx[i]][y+dy[i]].py=y;
				if(x+dx[i]==5&&y+dy[i]==5) return;
				maze[x+dx[i]][y+dy[i]].block=1;
				Q.push(maze[x+dx[i]][y+dy[i]]);
			}
		} 
	}
}

stack<unit> S;

int way(){//从终点开始将父节点压入栈直到起点 
	int x,y,x2,y2;
	int cot=0;
	x=y=5;
	while(x!=1||y!=1){
		S.push(maze[x][y]);
		x2=maze[x][y].px;
		y2=maze[x][y].py;
		x=x2;
		y=y2;
		cot++;
	}
	S.push(maze[1][1]);
	return cot;
}

void output(){//输出坐标(恢复偏移) 
	int x,y;
	unit U;
	while(!S.empty()){
		U=S.top();
		S.pop();
		x=U.x;
		y=U.y;
		cout<<"("<<x-1<<", "<<y-1<<")"<<endl;
	}
}


int main(){
	for(int i=0;i<=6;i++)//为迷宫边界block置1 
		maze[i][0].block=maze[0][i].block=maze[i][6].block=maze[6][i].block=1;
	for(int i=1;i<=5;i++){//输入迷宫 
		for(int j=1;j<=5;j++){
			cin>>maze[i][j].block;
			maze[i][j].x=i;
			maze[i][j].y=j;
		}
	}
	bfs();
	way();
	output();
}

B - Pour Water

题意

倒水问题 “fill A” 表示倒满A杯,"empty A"表示倒空A杯,“pour A B” 表示把A的水倒到B杯并且把B杯倒满或A倒空。
输入包含多组数据。每组数据输入 A, B, C 数据范围 0 < A <= B 、C <= B <=1000 、A和B互质。
你的程序的输出将由一系列的指令组成。这些输出行将导致任何一个罐子正好包含C单位的水。


思路:

本题是对隐式图搜索的练习。

题目可以转化为如下隐式图:已知起点为(A,B)=(0,0),终点为(A,B)=(0,C)(A,B)=(C,0)(若C≤A)。结点间的相邻关系即fill、empty、pour三组操作所能产生的改变。

设计了结构体状态status,含有成员变量AB状态,pApB父状态,pO父状态变为此状态所做的操作,reach到达标记。

设计了非成员函数fill()empty()pour(),三个函数所需参数相同,需要当前状态int aint b,操作类型int ope,引用int& xint& y传出目标状态;都返回布尔值判断目标结点是否已到达。

bfs、寻路、输出过程与上一题相似。


总结&疑问:

本题联系了广度优先搜索中的一种特殊类型隐式图,节点并非由给出的边连接而是根据给出的关系在搜索过程中显露出来。本题依然穷举了所有的状态,开辟为数组并储存,但本题数据范围较小可如此操作,若隐式图节点无法穷举,应该如何解决?


代码:

#include<iostream>
#include<queue>
#include<stack>
using namespace std;

int cubageA,cubageB,C;

struct status{//隐式图节点-状态 
	int A,B;// 状态(坐标) 
	int pA,pB,pO;// 父状态(父坐标) 
	bool reach;//到达标记 
	
	status(){
		A=B=0;
		pA=pB=pO=0;
		reach=0;
	}
	
	void set(int a,int b,int pa,int pb,int po){//设置节点状态 
		A=a;
		B=b;
		pA=pa;
		pB=pb;
		pO=po;
		
		reach=1;
	}
	
	void clean(){//重置 
		A=B=pA=pB=pO=0;
		reach=0;
	}
};

status R[1050][1050];//枚举全部状态 

bool fill(int a,int b,int ope,int&x,int&y){//fill操作:a、b为当前状态,ope表示fillA/fillB,x、y传出目标状态 
	int toA,toB,to;
	if(ope==1){//fillA 
		toA=cubageA;
		toB=b;
		to=1;
	}
	else if(ope==2){//fillB
		toA=a;
		toB=cubageB;
		to=2;
	}
	if(R[toA][toB].reach)return 0;//返回是否已到达,为bfs函数分支给出判断;下同理 
	R[toA][toB].set(toA,toB,a,b,to);
	x=toA;
	y=toB;
	return 1;
}
	
bool empty(int a,int b,int ope,int&x,int&y){//empty操作:a、b为当前状态,ope表示emptyA/emptyB,x、y传出目标状态 
	int toA,toB,to;
	if(ope==1){
		toA=0;
		toB=b;
		to=3;
	}
	else if(ope==2){
		toA=a;
		toB=0;
		to=4;
	}
	if(R[toA][toB].reach)return 0;
	R[toA][toB].set(toA,toB,a,b,to);
	x=toA;
	y=toB;
	return 1;
}
	
bool pour(int a,int b,int ope,int&x,int&y){//pour操作:a、b为当前状态,ope表示pourAB/pourBA,x、y传出目标状态 
    int toA,toB,to;
	if(ope==1){
    	if(a<=(cubageB-b)){
			toB=a+b;
			toA=0;
		}
		else {
			toB=cubageB;
			toA=a-cubageB+b;
		}
		to=5;
	}
	else if(ope==2){
		if(b<=(cubageA-a)){
			toA=a+b;
			toB=0;
		}
		else {
			toA=cubageA;
			toB=b-cubageA+a;
		}
		to=6;
	}
	if(R[toA][toB].reach)return 0;
	R[toA][toB].set(toA,toB,a,b,to);
	x=toA;
	y=toB;
	return 1;
}

void clear(int a,int b){//清空 
	for(int i=0;i<=a+5;i++){
		for(int j=0;j<=b+5;j++){
			R[i][j].clean();
		}
	}
}

queue<status*> Q;

status* bfs(){
	status* temp=&R[0][0];
	int a,b;
	Q.push(temp);
	while(!Q.empty()){
		temp=Q.front();
		Q.pop();
		if(temp->A==C||temp->B==C)return temp;
		
		//生成六种变化的方向,并判断是否要加入队列 
		if(fill(temp->A,temp->B,1,a,b))Q.push(&R[a][b]);
		if(fill(temp->A,temp->B,2,a,b))Q.push(&R[a][b]);
		if(empty(temp->A,temp->B,1,a,b))Q.push(&R[a][b]);
		if(empty(temp->A,temp->B,2,a,b))Q.push(&R[a][b]);
		if(pour(temp->A,temp->B,1,a,b))Q.push(&R[a][b]);
		if(pour(temp->A,temp->B,2,a,b))Q.push(&R[a][b]);
	}
}

stack<int> S;

void way(status* ans){//回溯父状态,生成路径 
	while(ans->pO!=0){
		S.push(ans->pO);
		ans=&R[ans->pA][ans->pB];
	}
}

void output(){//取出栈,依照格式输出 
	int ope;
	while(!S.empty()){
		ope=S.top();
		S.pop();
		if(ope==1)cout<<"fill A"<<endl;
		else if(ope==2)cout<<"fill B"<<endl;
		else if(ope==3)cout<<"empty A"<<endl;
		else if(ope==4)cout<<"empty B"<<endl;
		else if(ope==5)cout<<"pour A B"<<endl;
		else if(ope==6)cout<<"pour B A"<<endl;
	}
	cout<<"success"<<endl;
}

int main(){
	while(cin>>cubageA){
		cin>>cubageB>>C;
		
		clear(cubageA,cubageB);
		R[0][0].reach=1;
		status* ans=bfs();
		way(ans);
		output();
	} 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值