程序设计思维与实践 Week2 作业

A题——迷宫路径问题(bfs)

问题描述

东东有一张地图,想通过地图找到妹纸。地图显示,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)

思路

每个点用pair<int,int>结构存储,用二维数组a存储迷宫的结构,同样的vist数组存储是否访问过该点,dx与dy存储x、y方向的对应偏移量(宽度为1),一个点加上两轴偏移量为它的邻接点。
从起点(0,0)开始,向四周邻接点扩宽,如果未访问该点且为放置障碍物,则该点入队并标记已访问。
再按照队列中的次序按上述规则访问,每次访问完弹出,直到到达终点(4,4)或队列空(不存在路径从起点到终点)。
要输出路径经过的点,可以设置一个map<pair<int,int>,pair<int,int> >型的数组Path1,序号为该点,对应值为该点在最终路径上连接的前一个点。此时可以采用回溯的方式,从终点一直回溯到起点入栈S,这样可以避免多余点的输出。最后从栈S中一一输出弹出点,即为最终的路径。

总结

一开始用回溯的方法取出的顺序是从终点到起点,这时栈的作用在于自然将输出顺序变为起点到终点。注意map的使用,同类型的映射可以进行回溯。

代码

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

bool visit[5][5];//用来记录是否到达该点 
int a[5][5];//存储迷宫 
int dis;

int dx[]={0,0,1,-1};//一个点的邻近点偏移量 
int dy[]={1,-1,0,0};

 


queue<pair<int,int> > Q; 
map<pair<int,int>,pair<int,int> >  Path1;
stack<pair<int,int> > S;


void bfs()
{
	pair<int,int> start(0,0);
	pair<int,int> end(4,4);
	Q.push(start);//起点入队

	visit[0][0]=true;
	while(!Q.empty()) 
	{
		pair<int,int> current=Q.front();
		Q.pop(); 
		for(int i=0;i<4;i++)
		{
			int theX=current.first+dx[i];
			int theY=current.second+dy[i];
			//当邻接点在地图内,且未被访问及无障碍时,入队列 
			if(theX>=0 && theX<5 && theY>=0 && theY<5
				&& !visit[theX][theY] && !a[theX][theY])
			{
				visit[theX][theY]=true;
				pair<int,int> p1(theX,theY);
				Q.push(p1); 
				pair<int,int> p2(current.first,current.second);//到达这个点经过的前一个点 
				Path1[p1] = p2;//点为下标时,数组对应值为它路径上的前一个点 
			}
			if((theX==4&&theY==5)||(theX==5&&theY==4))//到达终点则退出循环 
				break;
		}
	}
	
	pair<int,int> rear=end;
	S.push(rear);
	while(rear!=start)
	{
		
		int x=Path1[rear].first;
		int y=Path1[rear].second;
		pair<int,int> p3(x,y); 
		rear=p3;//将路径从终点回溯到起始点 
		S.push(rear);
	}	

}

//输入迷宫阵型 
void design()
{
	for(int i=0;i<5;i++)
	{
		for(int j=0;j<5;j++)
		{
			cin>>a[i][j];
		}
	}
}	

void show()
{
	pair<int,int> result;
	while(!S.empty())
	{
		result=S.top();
		cout<<"("<<result.first<<", "<<result.second<<")"<<endl;
		S.pop();
	}
}

int main()
{
	design();
	bfs();
	show();
	return 0;
}

B题——倒水问题(bfs)

问题描述

倒水问题 “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

思路

跟上一题类似,从一开始的两个空杯到最终的(xx,C)或(C,yy),对于每种操作都可以看做是一种状态(点)到达另一种状态(点)的偏移量。status结构体作为一种状态,存储两个杯子分别的装水量,设置map<status,bool>的映射关系,存储一种状态是否存在过的情况,设置map<status,status>的映射关系,存储末状态对应的始状态(前一种状态,不是初始状态)。就可以用递归的方式进行回溯输出,在判断输出什么操作字符串时,用了一种比较暴力的方法,将这种状态与map中对应的始状态进行运算比较,由此推断出由始状态到末状态的操作是什么。

总结

需要回溯时,map是一个好帮手。本来想以状态映射字符串的形式来判断操作并输出,但试了发现余力不足,还是回到了简单的运算判断。

代码

#include <iostream>
#include <map>
#include <queue>


using namespace std;

int A,B,C;
struct status{
	int a,b;//A、B杯的装水状态 
	status()
	{//初始都为空杯 
		a=0;b=0;
	} 
	status(int aa,int bb)
	{
		a=aa;b=bb;
	}
	bool operator<(const status &X) const 
	{
        return a == X.a ? b < X.b : a < X.a;
    }

	bool operator==(const status &X) const
	{
		return a==X.a && b==X.b;
	}
};


queue<status> Q;
map<status,bool> achieve;
map<status,status> way;


void show(status t) 
{
    if (way.find(t) != way.end()) {
        show(way[t]);
        if(way[t].a>0 && t.a==0 && way[t].b==t.b)
        	cout<<"empty A"<<endl;
        if(way[t].a==t.a && way[t].b>0 && t.b==0)
        	cout<<"empty B"<<endl;
        if(way[t].a!=A && t.a==A && way[t].b==t.b)
        	cout<<"fill A"<<endl;
    	if( way[t].a==t.a && way[t].b!=B && t.b==B)
        	cout<<"fill B"<<endl;
        if(t.a==max((way[t].a+way[t].b-B),0) && t.b==min(B,way[t].a+way[t].b))
        	cout<<"pour A B"<<endl;
        if(t.a==min(A,way[t].a+way[t].b)  && t.b==max((way[t].a+way[t].b-A),0))
        	cout<<"pour B A"<<endl;
    } 

}

void bfs()
{

	status start(0,0);
	Q.push(start);
	achieve[start]=true;
	while(!Q.empty())
	{
		status current=Q.front();
		Q.pop();
		if(current.a==C || current.b==C)
		{
			show(current);
			return;
		}
		
		status 	EmptyA(0,current.b),
				EmptyB(current.a,0),
				FillA(A,current.b),
				FillB(current.a,B),
			AToB(max((current.a+current.b-B),0) , min(B,current.a+current.b)),
			BToA(min(A,current.a+current.b) , max((current.a+current.b-A),0));
		
		
		if(!achieve[EmptyA])
		{
			achieve[EmptyA]=true;
			way[EmptyA]=current; //下标为末状态,对应值为始状态
			Q.push(EmptyA); 
			
		}	
		if(!achieve[EmptyB])
		{
			achieve[EmptyB]=true;
			way[EmptyB]=current; //下标为末状态,对应值为始状态
			Q.push(EmptyB); 
			
		}
		
		if(!achieve[FillA])
		{
			achieve[FillA]=true;
			way[FillA]=current; //下标为末状态,对应值为始状态
			Q.push(FillA); 
			
		}
		
		if(!achieve[FillB])
		{
			achieve[FillB]=true;
			way[FillB]=current; //下标为末状态,对应值为始状态
			Q.push(FillB); 
			
		}
		if(!achieve[AToB])
		{
			achieve[AToB]=true;
			way[AToB]=current; //下标为末状态,对应值为始状态
			Q.push(AToB); 
			
		}
		if(!achieve[BToA])
		{
			achieve[BToA]=true;
			way[BToA]=current; //下标为末状态,对应值为始状态
			Q.push(BToA); 
			
		}
		
	}
	
}

int main()
{
	while(cin>>A>>B>>C)
	{
		achieve.clear();
		way.clear();
		while(!Q.empty())
			Q.pop();
		bfs();
		cout<<"success"<<endl;
	}
	
	return 0;
}





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值