2.1 搜索专题
-----石泉 组题
A. HDU 1035 Robot Motion
原地址 : http://acm.hdu.edu.cn/showproblem.php?pid=1035
题目::
A robot has been programmed to follow the instructions in its path. Instructions for the next direction the robot is to move are laid down in a grid. The possible instructions are
N north (up the page)
S south (down the page)
E east (to the right on the page)
W west (to the left on the page)
For example, suppose the robot starts on the north (top) side of Grid 1 and starts south (down). The path the robot follows is shown. The robot goes through 10 instructions in the grid before leaving the grid.
Compare what happens in Grid 2: the robot goes through 3 instructions only once, and then starts a loop through 8 instructions, and never exits.
You are to write a program that determines how long it takes a robot to get out of the grid or how the robot loops around.
Input
There will be one or more grids for robots to navigate. The data for each is in the following form. On the first line are three integers separated by blanks: the number of rows in the grid, the number of columns in the grid, and the number of the column in which the robot enters from the north. The possible entry columns are numbered starting with one at the left. Then come the rows of the direction instructions. Each grid will have at least one and at most 10 rows and columns of instructions. The lines of instructions contain only the characters N, S, E, or W with no blanks. The end of input is indicated by a row containing 0 0 0.
Output
For each grid in the input there is one line of output. Either the robot follows a certain number of instructions and exits the grid on any one the four sides or else the robot follows the instructions on a certain number of locations once, and then the instructions on some number of locations repeatedly. The sample input below corresponds to the two grids above and illustrates the two forms of output. The word “step” is always immediately followed by “(s)” whether or not the number before it is 1.
Sample Input
3 6 5
NEESWE
WWWESS
SNWWWW
4 5 1
SESWE
EESNW
NWEEN
EWSEN
0 0
Sample Output
10 step(s) to exit
3 step(s) before a loop of 8 step(s)
题意::
一个机器人在一个n*m的方格内移动,方格内每个点都有意条指令N 向上走一步,S向下走一步,W向左走一步,E向`走一步。以第一行为行 输入n,m,s.way[1][s],为机器人的起始位置,
问机器人多少步能走出这个方格cout<<10 step(s) to exit,如果重复走同一格cout<<3 step(s) before a loop of 8 step(s) .
解题思路::
用DFS来遍历每一个节点,并且记录是否遍历过vis[i][j]=setp,判断边界条件,不重复满足输。重复求前当前的步数当前step-vis[i][j]. 主要DFS遍历 并且判断边界条件。
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
char str[999][999];
int a,b,s;
int step;
int vis[999][999]= {
0};// 初始化为0;
void DFS(int i,int j)
{
if(i<=0||i>a||j<=0||j>b) {
// 超出边界 停止输出
cout<<step<<" step(s) to exit"<<endl;
return ;
}
step++; 遍历一个步数+1;
if(vis[i][j]>0) {
// 判断是否重复遍历过
int k=vis[i][j];
cout<<k-1<<" step(s) before a loop of "<<step-k<<" step(s)"<<endl;
return ;
}
vis[i][j]=step; //判断四个方向,判断该往哪个方向移动
if(str[i][j]=='W')
DFS(i,j-1);
else if(str[i][j]=='S')
DFS(i+1,j);
else if(str[i][j]=='N')
DFS(i-1,j);
else if(str[i][j]=='E')
DFS(i,j+1);
}
int main()
{
while(cin>>a>>b) {
if(a==0&&b==0)
break;
step=0;//初始化步数为0;
cin>>s;
memset(vis,0,sizeof(vis));
int i,j;
for(i=1; i<=a; i++)
for(j=1; j<=b; j++)
cin>>str[i][j];
DFS(1,s); // 由第一行第s列开始
}
return 0;
}
B. OpenJ_Bailian 2748 全排列
原地址: http://bailian.openjudge.cn/practice/2748?lang=en_US
题目::
给定一个由不同的小写字母组成的字符串,输出这个字符串的所有全排列。 我们假设对于小写字母有’a’ < ‘b’ < … < ‘y’ < ‘z’,而且给定的字符串中的字母已经按照从小到大的顺序排列。
Input
输入只有一行,是一个由不同的小写字母组成的字符串,已知字符串的长度在1到6之间。
Output
输出这个字符串的所有排列方式,每行一个排列。要求字母序比较小的排列在前面。字母序如下定义:
已知S = s 1s 2…s k , T = t 1t 2…t k,则S < T 等价于,存在p (1 <= p <= k),使得
s 1 = t 1, s 2 = t 2, …, s p - 1 = t p - 1, s p < t p成立。
Sample Input
abc
Sample Output
abc
acb
bac
bca
cab
cba
dfs+奇偶剪枝+回溯
解题思路::常规的dfs题型写法,需要注意剪枝。第一步剪枝就是判断全都是路的情况给的时间都小于最短到达的时间,就不用搜索判断一下直接输出;第二步奇偶剪枝比较难发现,搜索过程中剩余时间(t-step)减去到达终点的所需时间为奇数时就不可能到达终点。
//这个题解copy别人的 自己要看懂。
/*OpenJ_Bailian 2748*/
#include<bits/stdc++.h>
using namespace std;
char s[10];
int num=0;
char mapp[10];
int vis[10];
void dfs(char a)
{
if(num==strlen(s)-1 ) //如果num与s的长度相等 表示搜索到头了
{
for(int i=0; i<=num; i++)// 遍历输出mapp数组然后返回
{
cout<<mapp[i];
}
putchar(10);//输出回车
return;
}
for(int i=0; i<strlen(s); i++) // i 从0开始按字典序对下一个字符进行深搜
{
if(vis[i]==0)//如果s[i]没有被访问过就对是s[i]深搜
{
num++;
vis[i]=1;//标记已访问
mapp[num]=s[i]; //访问后把s[i]存储在mapp里
dfs(s[i]);//对s[i]深搜
vis[i]=0;//回溯
num--;//同上
}
}
}
int main()
{
while(cin>>s)
{
sort(s,s+strlen(s)); //从小到大给数组s排序
for(int i=0; i<strlen(s); i++) // 按字典序挨个对s[i]深搜
{
num=0;// num存储是当前第几个数据
mapp[0]=s[i]; //把深搜出来的字符存到mapp数组里
vis[i]=1; //遍历后标记被访问过
dfs(s[i]);//对s[i]深搜】
memset(vis,0,sizeof(vis)); //深搜结束后把vis 和 mapp 清空
memset(mapp,'0',sizeof(mapp));
}
}
return 0;
}
自己AC代码::
解题思路::利用c++中STL中的函数全排列next_permutation(str,str+n)重下标为0到n-1的全排列函数实现。
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
char str[10];
int main()
{
int n;
while(scanf("%s",str)!=EOF)//录入字符串
{
n=strlen(str);
do //循环输出
{
printf("%s\n",str);
}while(next_permutation(str,str+n));
}
return 0;
}
//OK
C. HDU 1010 Tempter of the Bone
原地址: http://acm.hdu.edu.cn/showproblem.php?pid=1010
题目::
The doggie found a bone in an ancient maze, which fascinated him a lot. However, when he picked it up, the maze began to shake, and the doggie could feel the ground sinking. He realized that the bone was a trap, and he tried desperately to get out of this maze.
The maze was a rectangle with sizes N by M. There was a door in the maze. At the beginning, the door was closed and it would open at the T-th second for a short period of time (less than 1 second). Therefore the doggie had to arrive at the door on exactly the T-th second. In every second, he could move one block to one of the upper, lower, left and right neighboring blocks. Once he entered a block, the ground of this block would start to sink and disappear in the next second. He could not stay at one block for more than one second, nor could he move into a visited block. Can the poor doggie survive? Please help him.
Input
The input consists of multiple test cases. The first line of each test case contains three integers N, M, and T (1 < N, M < 7; 0 < T < 50), which denote the sizes of the maze and the time at which the door will open, respectively. The next N lines give the maze layout, with each line containing M characters. A character is one of the following:
‘X’: a block of wall, which the doggie cannot enter;
‘S’: the start point of the doggie;
‘D’: the Door; or
‘.’: an empty block.
The input is terminated with three 0’s. This test case is not to be processed.
Output
For each test case, print in one line “YES” if the doggie can survive, or “NO” otherwise.
Sample Input
4 4 5
S.X.
…X.
…XD
…
3 4 5
S.X.
…X.
…D
0 0 0
Sample Output
NO
YES
题意:
doggie 在一个古老的迷宫发现了一块骨头,他捡起骨头,发现是个陷阱,它中了陷阱,doggie想逃出迷宫,同时也知道地图,问你doggie能否逃出去?其中地图 S 为doggie的位置 ’ . '为可以走的路线,X 为doggie翻越不了的围墙,D是出口的门。 输入n*m大的迷宫 t为D门打开的时间点,输出NO / YES doggie 能否逃出去。
解题思路
DFS+回溯+剪枝 遍历每一个点,利用计数器step记录步数,也就是到达D的时间,判断边界条件 ,其中重点,记录X的个数 并且判断 这个式子if(t>n*m-sum-1) {cout<<"NO"<<endl; continue;}
直接判断,不然很容易TLE,好要记得的访问过,将这个点回溯,清除标记,以便于下一次遍历(回溯)。
AC代码::
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int n,m,t;
char str[9][9];
int vis[9][9]= {
0};
int dx,dy;
int g; // 思路正确 TLE 剪枝不够
void DFS(int x,int y,int step)//传入当前位置,和走的步数
{
int fix[4][2]= {
{
0,1},{
0,-1},{
1,0},{
-1,0}};//来个行走的方向
if(x==dx&&y==dy) {
//判断是否到达了位置
if(t==step) //时间是否刚好合适
g=1;
return ; //按条件返回
}
if(g)
return ;
if(str[x][y]=='X')
return ; // 遇到X反回上一步操
for(int i=0; i<4; i++) {
//向四个方向遍历
int fx=x+fix[i][0],fy=y+fix[i][1];
if(fx<0||fx>=n||fy<0||fy>=m)//到达边界换一个方向
continue; //省的在DFS一次 减少时间
if(vis[fx][fy]==0) {
//满足条件 向下遍历