A - Maze
题目
Input&&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
#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)
题解
1.本题是一道迷宫题因此采用宽搜的方法进行对地图的遍历(队列实现)
2.由于需要输出路径因此我们记录从开始到达每一个节点需要多少步,最后只需要按照步数的逆就可以输出路径(利用路径矩阵 其中也包含了地图的其他信息 如墙)
3.如何加入节点,每个节点“附近”有四个方向可走我们只需要依次判断这些节点的合法性就可以判断是否加入队列
4.判断是否可走依据:
1)不超过地图边界
2)没有到达过(初始化为-1 表示未到达过)
3)没有穿墙术(墙标记为-2)
5.实际上也可以加上到达终点后返回
C++代码
#include<iostream>
#include<queue>
using namespace std;
const int m = 5,n = 5;
int M[m][n];//m*n
void Read(){
for(int i = 0;i<m;i++)
for(int j = 0;j<n;j++){
cin>>M[i][j];
if(M[i][j] == 0){
M[i][j] = -1;//标识未访问过
}else{
M[i][j] = -2;//墙
}
}
}
struct P{
int x;
int y;
bool operator!=(P &p){
return x!=p.x||y!=p.y;
}
};
queue<P> que;
int dx[] = {0,0,1,-1};//四个方向
int dy[] = {1,-1,0,0};
void output(P end){
int num = M[end.x][end.y];
if(num == 0){
cout<<"("<<end.x<<", "<<end.y<<")\n";
return;
}
for(int i = 0;i<4;i++){
P current = {end.x+dx[i],end.y+dy[i]};
if(current.x>=0&¤t.x<=n&¤t.y>=0&¤t.y<=m){
if(M[current.x][current.y] == num-1){
output(current);
cout<<"("<<end.x<<", "<<end.y<<")\n";
break;
}
}
}
}
void bfs(P begin,P end){
M[begin.x][begin.y] = 0;
que.push(begin);
while(!que.empty()){
P current = que.front();
que.pop();
for(int i = 0;i<4;i++){
int x = current.x+dx[i];
int y = current.y+dy[i];
if(x>=0&&x<=n&&y>=0&&y<=m){//在区域内
if(M[x][y]!=-2&&M[x][y]==-1){//不是墙且没到达过
que.push({x,y});
M[x][y] = 1+M[current.x][current.y];
}
}
}
}
output(end);
}
int main(){
P begin = {0,0};
P end = {4,4};
Read();
bfs(begin,end);
return 0;
}
B - Pour Water(本题多解 输出一种即可)
题目
Input&&Output:
Sample:
#input:
2 7 5
2 7 4
#output:
fill B
pour B A
success
fill A
pour A B
fill A
pour A B
success
题解
1.本题如果将水量看作一种状态,可以发现它的变化是一个状态到另一个状态的改变,对于这种题目我们可以想到利用宽搜的方法去遍历它的所有状态。
2.本题有AB两个水杯我们可以把他们的水量看作一个二维坐标,利用队列去存储这些状态
3.由于需要输出过程(路径),本题我采用了一个map(from)去记录每种状态的来源状态以及对应的操作(利用结构体存储)
4.状态如何变化:
每种状态分别有六种变化:
1)A倒空
2)B倒空
3)A倒满
4)B倒满
5)A倒入B(又分为两种小状态 A全部被倒入B ; A倒满B有剩余)
6)B倒入A(同样两种小状态 B全部被倒入A ; B倒满A有剩余)
5.如何加入:每种状态我们需要判断它的合法性才能判断是否加入队列,并在from中加入这种状态
6.合法性判断:
1)此状态未到达过
2)没有出现最终结果
7.结果的输出:利用递归从from中依次调出每次状态的变化并输出。
C++代码
#include<iostream>
#include<map>
#include<string>
#include<queue>
using namespace std;
const int MAXA = 1005,MAXB = 1005;
int mark[MAXA][MAXB];
void init(int a,int b){
for(int i = 0 ;i<=a;i++)
for(int j = 0;j<=b;j++)
mark[i][j] = 0;//表示未出现过
}
struct state{
int a;
int b;
string op;//来到这一步进行的操作
};
struct P{
int a;
int b;
bool operator<(const P &x) const{
if(a!=x.a) return a<x.a;
return b<x.b;
}
};
map<P,state> from;//记录某一个状态的上一步 状态a来自状态b
queue<P> que;
void ins(state fro,P current){
if(mark[current.a][current.b] == 0){
from[current] = fro;
que.push(current);
mark[current.a][current.b] = 1;
}
}
void output(P current){
state next = from[current];
//cout<<next.a<<" "<<next.b<<" "<<next.op<<endl;
if(next.a==0&&next.b==0){
cout<<next.op;
return;
}
P go = {next.a,next.b};
output(go);
cout<<next.op;
}
void water(int a,int b,int c){
init(a,b);
mark[0][0] = 1;
P current = {0,0};
state fro;
que.push(current);
while(!que.empty()){
P now = que.front();que.pop();
//倒空a
if(now.a!=0&&mark[0][now.b]==0){//a中有水并且之后的状态未出现过
fro = {now.a,now.b,"empty A\n"};
current = {0,now.b};
ins(fro,current);
if(current.a == c||current.b == c){//找到了 输出 从这一个向上找
output(current);
break;
}
}
//倒空b
if(now.b!=0&&mark[now.a][0]==0){
fro = {now.a,now.b,"empty B\n"};
current = {now.a,0};
ins(fro,current);
if(current.a == c||current.b == c){//找到了 输出 从这一个向上找
output(current);
break;
}
}
//倒满a
if(now.a!=a&&mark[a][now.b] == 0){
fro = {now.a,now.b,"fill A\n"};
current = {a,now.b};
ins(fro,current);
if(current.a == c||current.b == c){//找到了 输出 从这一个向上找
output(current);
break;
}
}
//倒满b
if(now.b!=b&&mark[now.a][b]==0){
fro = {now.a,now.b,"fill B\n"};
current = {now.a,b};
ins(fro,current);
if(current.a == c||current.b == c){//找到了 输出 从这一个向上找
output(current);
break;
}
}
//a倒b
if(now.a!=0){
fro = {now.a,now.b,"pour A B\n"};
if(now.a+now.b<=b&&mark[0][now.a+now.b]==0){//a倒入b无剩余
current = {0,now.a+now.b};
}else if(mark[now.a-(b-now.b)][b]==0){
current = {now.a-(b-now.b),b};
}
ins(fro,current);
if(current.a == c||current.b == c){//找到了 输出 从这一个向上找
output(current);
break;
}
}
//b倒a
if(now.b!=0){
fro = {now.a,now.b,"pour B A\n"};
if(now.a+now.b<=a&&mark[now.a+now.b][0]==0){//b倒入a无剩余
current = {now.a+now.b,0};
}else if(mark[a][now.b-(a-now.a)]==0){
current = {a,now.b-(a-now.a)};
}
ins(fro,current);
if(current.a == c||current.b == c){//找到了 输出 从这一个向上找
output(current);
break;
}
}
}
cout<<"success\n";
}
int main(){
int a,b,c;
while(cin>>a>>b>>c){
water(a,b,c);
from.clear();
while(!que.empty()) que.pop();
}
return 0;
}