https://blog.csdn.net/consciousman/article/details/54613292
POJ2488 – A Knight’s Journey【骑士游历】
大致题意
给出一个国际棋盘的大小,判断马能否不重复的走过所有格,并记录下其中按字典序排列的第一种路径。
经典的“骑士游历”问题,DFS水题一道
解题思路 难度不大,但要注意的地方有3点: 1、 题目要求以”lexicographically”方式输出,也就是字典序…要以字典序输出路径,那么搜索的方向(我的程序是path()函数)就要以特殊的顺序排列了…这样只要每次从dfs(A,1)开始搜索,第一个成功遍历的路径一定是以字典序排列… 下图是搜索的次序,马的位置为当前位置,序号格为测试下一步的位置的测试先后顺序 按这个顺序测试,那么第一次成功周游的顺序就是字典序 2、国际象棋的棋盘,行为数字p;列为字母q 3、网上有同学说 这道题最后一组数据后是有空行的会PE…,我测试过,不会的,能AC
//Memory Time
//240K 32MS
#include<iostream>
using namespace std;
typedef class
{
public:
int row;
char col;
}location;
int p,q; //chess size = p*q
//数字是行p,字母是列q
bool chess['Z'+1][27];
int x,y; //返回值
void path(int i,int j,int num) //ij为骑士当前在棋盘的位置
{ //num为骑士即将要跳到的位置序号
switch(num)
{
case 1: {x=i-1; y=j-2; break;} //注意这个尝试跳的顺序不能错
case 2: {x=i+1; y=j-2; break;} //因为题目要求是字典序lexicographically输出
case 3: {x=i-2; y=j-1; break;} //这个顺序错了,必定WA
case 4: {x=i+2; y=j-1; break;}
case 5: {x=i-2; y=j+1; break;}
case 6: {x=i+2; y=j+1; break;}
case 7: {x=i-1; y=j+2; break;}
case 8: {x=i+1; y=j+2; break;}
}
return;
}
bool DFS(location* way,int i,int j,int step)
{
chess[i][j]=true;
way[step].row=i;
way[step].col=j;
if(step==way[0].row)
return true;
for(int k=1;k<=8;k++) //骑士从当前位置尝试跳到其他位置
{
path(i,j,k);
int ii=x,jj=y;
if(!chess[ii][jj] && ii>=1 && ii<=p && jj>='A' && jj<='A'+q-1)
if(DFS(way,ii,jj,step+1))
return true;
}
chess[i][j]=false; //能执行到这步,说明前面跳的8步都不符合要求
return false; //即当前位置是错误位置,擦除记录返回上一步
}
int main(void)
{
int test;
cin>>test;
int t=1;
while(t<=test)
{
/*Initial*/
memset(chess,false,sizeof(chess));
cin>>p>>q;
if(p==1 && q==1) //范围缩窄,不要也能AC
{
cout<<"Scenario #"<<t++<<':'<<endl;
cout<<"A1"<<endl<<endl;
continue;
}
if(p*q>26 || p>=9 || q>=9 || p<=2 || q<=2) //范围缩窄,不要也能AC
{
cout<<"Scenario #"<<t++<<':'<<endl;
cout<<"impossible"<<endl<<endl;
continue;
}
location* way=new location[p*q+1]; //记录走过的位置坐标
way[0].row=p*q; //记录总步数(棋盘总格子数)
/*DFS*/
bool flag=false;
for(int j='A';j<='A'+q-1;j++)
{
for(int i=1;i<=p;i++)
if(DFS(way,i,j,1))
{
cout<<"Scenario #"<<t++<<':'<<endl;
for(int k=1;k<=way[0].row;k++)
cout<<way[k].col<<way[k].row;
cout<<endl<<endl;
flag=true;
break;
}
if(flag)
break;
}
if(!flag)
{
cout<<"Scenario #"<<t++<<':'<<endl;
cout<<"impossible"<<endl<<endl;
}
}
return 0;
}
2、poj3083
题目大意
给定一个迷宫,S是起点,E是终点,”#”是墙不可走,”.”可以走
先输出左转优先时,从S到E的步数
再输出右转优先时,从S到E的步数
最后输出S到E的最短步数
W为宽,列数 H为高,行数(3 <= w, h <= 40)
一个“S”和一个“E”将出现在迷宫中,它们将始终位于迷宫的一个边缘,而不是一个角落。迷宫将完全由墙壁('#')包围,唯一的开口是'S'和'E'。“S”和“E”之间还将至少隔一堵墙(“#”)。
Sample Input
2
8 8
########
#......#
#.####.#
#.####.#
#.####.#
#.####.#
#...#..#
#S#E####
9 5
#########
#.#.#.#.#
S.......E
#.#.#.#.#
#########
Sample Output
37 5 5
17 17 9
解题思路
DFS和BFS的综合题水题,难度不大,但是写代码时要注意几方面:
1、 左转、右转优先搜索时必须标记当前位置时的方向,我定义的方向是
最初的方向由起点S确定,而下一步的方向则由前一步的走向决定
例如 左边优先搜索:
当前位置的方向指向 1(向左),(这同时说明前一步是在第“3”的位置走过来的)
那么走下一步时,就要根据2103的顺序,先逐格确定当前位置周边的四格是否可行
若第一次确认2可行,就走到2,在位置2时的方向为2(向下)
若2不可行,则再确定1,若1可行,就走到1,在位置1时的方向为1(向左)
若1也不可行,则再确定0,若0可行,就走到0,在位置0时的方向为0(向上)
若0也不可行,说明进入了迷宫的死胡同,要从原路返回,走回3
右边优先搜索也同理。
根据我定义的方向,设当前位置为d,那么
左转,用数学式子表达就是 d=(d+1)%4
右转,用数学式子表达就是 d=(d+3)%4
我比较懒,在我的程序中,DFS和BFS都用了多入口的做法,有兴趣的同学可以利用我给出的这两个式子对代码进行优化。
这里有一点必须要注意的:
左边、右边优先搜索都不是找最短路,因此走过的路可以再走,无需标记走过的格
2、寻找最短路只能用BFS
因此在做第3问时别傻乎乎的又用DFS,DFS对于样例的输入确实和BFS得到的结果一样的,别以为样例PASS就提交了。。。所以我就说样例没代表性,学会测试数据很重要= =
注意有一点:
要求E的最短路,必须把迷宫模拟为树,S为根,找到E所在的层(树深),该层就是S到E的最短路,处理技巧就是在BFS时,令queue[tail]的depth等于对应的queue[head]的depth+1,详细见我的程序
把循环的次数作为深度就铁定错的
//Memory Time
// 212K 0MS
#include<iostream>
using namespace std;
typedef class
{
public:
int r,c;
int depth;
}SE;
SE s,e; //起止点
int Lstep; //左边优先搜索 时从S到E的总步数
int Rstep; //右边优先搜索 时从S到E的总步数
int shortstep; //S到E的最少总步数
bool maze[41][41]; //记录迷宫的“可行域”与“墙”
void DFS_LF(int i,int j,int d) //左边优先搜索,i,j为当前点坐标,d为当前位置方向
{
Lstep++;
if(i==e.r && j==e.c)
return;
switch(d)
{
case 0:
{
if(maze[i][j-1])
DFS_LF(i,j-1,1);
else if(maze[i-1][j])
DFS_LF(i-1,j,0);
else if(maze[i][j+1])
DFS_LF(i,j+1,3);
else if(maze[i+1][j])
DFS_LF(i+1,j,2);
break;
}
case 1:
{
if(maze[i+1][j])
DFS_LF(i+1,j,2);
else if(maze[i][j-1])
DFS_LF(i,j-1,1);
else if(maze[i-1][j])
DFS_LF(i-1,j,0);
else if(maze[i][j+1])
DFS_LF(i,j+1,3);
break;
}
case 2:
{
if(maze[i][j+1])
DFS_LF(i,j+1,3);
else if(maze[i+1][j])
DFS_LF(i+1,j,2);
else if(maze[i][j-1])
DFS_LF(i,j-1,1);
else if(maze[i-1][j])
DFS_LF(i-1,j,0);
break;
}
case 3:
{
if(maze[i-1][j])
DFS_LF(i-1,j,0);
else if(maze[i][j+1])
DFS_LF(i,j+1,3);
else if(maze[i+1][j])
DFS_LF(i+1,j,2);
else if(maze[i][j-1])
DFS_LF(i,j-1,1);
break;
}
}
return;
}
void DFS_RF(int i,int j,int d) //右边优先搜索,i,j为当前点坐标,d为当前位置方向
{
Rstep++;
if(i==e.r && j==e.c)
return;
switch(d)
{
case 0:
{
if(maze[i][j+1])
DFS_RF(i,j+1,3);
else if(maze[i-1][j])
DFS_RF(i-1,j,0);
else if(maze[i][j-1])
DFS_RF(i,j-1,1);
else if(maze[i+1][j])
DFS_RF(i+1,j,2);
break;
}
case 1:
{
if(maze[i-1][j])
DFS_RF(i-1,j,0);
else if(maze[i][j-1])
DFS_RF(i,j-1,1);
else if(maze[i+1][j])
DFS_RF(i+1,j,2);
else if(maze[i][j+1])
DFS_RF(i,j+1,3);
break;
}
case 2:
{
if(maze[i][j-1])
DFS_RF(i,j-1,1);
else if(maze[i+1][j])
DFS_RF(i+1,j,2);
else if(maze[i][j+1])
DFS_RF(i,j+1,3);
else if(maze[i-1][j])
DFS_RF(i-1,j,0);
break;
}
case 3:
{
if(maze[i+1][j])
DFS_RF(i+1,j,2);
else if(maze[i][j+1])
DFS_RF(i,j+1,3);
else if(maze[i-1][j])
DFS_RF(i-1,j,0);
else if(maze[i][j-1])
DFS_RF(i,j-1,1);
break;
}
}
return;
}
void BFS_MSS(int i,int j) //最短路搜索
{
bool vist[41][41]={false};
SE queue[1600];
int head,tail;
queue[head=0].r=i;
queue[tail=0].c=j;//queue[0]表示起点的坐标
queue[tail++].depth=1; //当前树深标记,这是寻找最短路的关键点
vist[i][j]=true;
while(head<tail)
{
SE x=queue[head++];//往下走一层
if(x.r==e.r && x.c==e.c)
{
cout<<x.depth<<endl;
return;
}
if(maze[x.r][x.c-1] && !vist[x.r][x.c-1])//左
{
vist[x.r][x.c-1]=true;//标记该点已经被访问
queue[tail].r=x.r;//插入队列
queue[tail].c=x.c-1;
queue[tail++].depth=x.depth+1;//队列深度加一,队列指针右移
}
if(maze[x.r-1][x.c] && !vist[x.r-1][x.c])//上
{
vist[x.r-1][x.c]=true;
queue[tail].r=x.r-1;
queue[tail].c=x.c;
queue[tail++].depth=x.depth+1;
}
if(maze[x.r][x.c+1] && !vist[x.r][x.c+1])//右
{
vist[x.r][x.c+1]=true;
queue[tail].r=x.r;
queue[tail].c=x.c+1;
queue[tail++].depth=x.depth+1;
}
if(maze[x.r+1][x.c] && !vist[x.r+1][x.c])//下
{
vist[x.r+1][x.c]=true;
queue[tail].r=x.r+1;
queue[tail].c=x.c;
queue[tail++].depth=x.depth+1;
}
}
return;
}
int main(int i,int j)
{
int test;
cin>>test;
while(test--)
{
int direction; //起点S的初始方向
int w,h; //size of maze
cin>>w>>h;
/*Initial*/
Lstep=1;
Rstep=1;
memset(maze,false,sizeof(maze));
/*Structure the Maze*/
for(i=1;i<=h;i++)
for(j=1;j<=w;j++)
{
char temp;
cin>>temp;
if(temp=='.')//可以走的路
maze[i][j]=true;
if(temp=='S')//起点
{
maze[i][j]=true;
s.r=i;
s.c=j;
if(i==h)//起点在最后一行
direction=0;
else if(j==w)//起点在最后一列
direction=1;
else if(i==1)//起点在第一行
direction=2;
else if(j==1)//起点在第一列
direction=3;
}
if(temp=='E')//终点
{
maze[i][j]=true;
e.r=i;
e.c=j;
}
}
/*Left First Search*/
switch(direction)
{
case 0: {DFS_LF(s.r-1,s.c,0); break;}//左边优先搜索,s.r-1,s.c为当前点坐标,d为当前位置方向
case 1: {DFS_LF(s.r,s.c-1,1); break;}
case 2: {DFS_LF(s.r+1,s.c,2); break;}
case 3: {DFS_LF(s.r,s.c+1,3); break;}
}
cout<<Lstep<<' ';
/*Right First Search*/
switch(direction)
{
case 0: {DFS_RF(s.r-1,s.c,0); break;}
case 1: {DFS_RF(s.r,s.c-1,1); break;}
case 2: {DFS_RF(s.r+1,s.c,2); break;}
case 3: {DFS_RF(s.r,s.c+1,3); break;}
}
cout<<Rstep<<' ';
/*Most Short Step Search*/
BFS_MSS(s.r,s.c);
}
return 0;
}
#include <cstdio>
#include <cstring>
int w, h, c, a;
int Sx, Sy, Ex, Ey;
int cnt, flag, tag;
int dir[4][2] = {0, -1, -1, 0, 0, 1, 1, 0};
char maze[50][50];
int vis[50][50];
int que[2500][2];
void DFS(int x, int y, int cnt) {
if (maze[x][y] == 'E') {
printf("%d ", cnt);
tag = 0;
return ;
}
if (tag) for (int i = a+c, j = 0; j < 4; j++, i -= c) {
if (i < 0) i = 3;
if (i > 3) i = 0;
int tx = x + dir[i][0];
int ty = y + dir[i][1];
if (tx > h || tx < 1 || ty > w || ty < 1) continue;
if (tag && !vis[tx][ty] && maze[tx][ty] != '#') {
a = i;
DFS(tx, ty, cnt+1);
}
}
}
void BFS() {
int fir = 0, sec = 0;
que[sec][0] = Sx;
que[sec++][1] = Sy;
int step = 1;
while(fir < sec && !vis[Ex][Ey]) {
int tmp = sec;
step++;
while(fir < tmp && !vis[Ex][Ey]) {
int x = que[fir][0];
int y = que[fir++][1];
for(int i = 0; i < 4; i++) {
int tx = x + dir[i][0];
int ty = y + dir[i][1];
if (tx > h || tx < 1 || ty > w || ty < 1) continue;
if(!vis[tx][ty] && maze[tx][ty] != '#') {
que[sec][0] = tx;
que[sec++][1] = ty;
vis[tx][ty] = 1;
}
}
}
}
printf("%d\n", step);
}
int main() {
int t, i, j;
scanf("%d", &t);
while (t--) {
memset(maze, 0, sizeof(maze));
memset(vis, 0, sizeof(vis));
scanf("%d %d", &w, &h);
getchar();
for (i = 1; i <= h; i++) {
for (j = 1; j <= w; j++) {
scanf("%c", &maze[i][j]);
if (maze[i][j] == 'S') Sx = i, Sy = j;
else if (maze[i][j] == 'E') Ex = i, Ey = j;
}
getchar();
}
for (i = 0; i < 4; i++)
if (maze[Sx+dir[i][0]][Sy+dir[i][1]] == '.') break;
vis[Sx][Sy] = 1;
a = i; tag = 1; c = -1;
DFS(Sx, Sy, 1);
a = i; c = 1; tag = 1;
DFS(Sx, Sy, 1);
BFS();
}
return 0;
}
POJ3009 – Curling 2.0
大致题意 要求把一个冰壶从起点“2”用最少的步数移动到终点“3” 其中0为移动区域,1为石头区域,冰壶一旦想着某个方向运动就不会停止,也不会改变方向(想想冰壶在冰上滑动),除非冰壶撞到石头1 或者 到达终点 3 注意的是: 冰壶撞到石头后,冰壶会停在石头前面,此时(静止状态)才允许改变冰壶的运动方向,而该块石头会破裂