这是ACM之路学习的第一个算法——搜索算法。类似的题目有(HDOJ):1010、1240、1241、1242、1072、 1253、1312、1372(以上题目类似于1010)and 1238、1239、1015、1016、1401、1515、1548等。其中,1238不仅是基本搜索算法题,也是字符串处理的经典题目(包括求反串、求子串、字符串查找、求字符串长度),1239是数值型搜索题。而最经典的还属于1010题,该题的重点是涉及了两种剪枝方法,一是奇偶剪枝、一是路径剪枝。
题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=1010
dfs code:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <cmath>
using namespace std;
const int MAXN = 10;
int n,m,t,sx,sy,dx,dy,wall;
char map[MAXN][MAXN];
int vis[MAXN][MAXN];
int flag;
int dir[4][2]={1,0,0,1,-1,0,0,-1};
int abs(int x){return x>0 ? x : -x;}
void DFS(int x,int y,int c){
if(c>t) return ;//路径剪枝:超出时间则终止
if(x<0 || y<0 || x>=n || y>=m) return ;//越界判断
if(flag==1 || (map[x][y]=='D' && t==c)){//满足条件则返回
flag=1;
return ;
}
int temp =abs(x-dx)+abs(y-dy); //起点到终点的步数
temp=t-temp-c; //剩余步数=时间-需要步数-已用时间时间
if(temp<0 || temp&1) return ;//奇偶剪枝:走的步数是偶数,且剩余时间大于零,才会满足条件
for(int i=0;i<4;++i){ //四个方向的搜索
int tx=x+dir[i][0];
int ty=y+dir[i][1];
if(!vis[tx][ty] && map[tx][ty]!='X'){
vis[tx][ty]=1;//走过就标记,防止再次访问
DFS(tx,ty,c+1);
vis[tx][ty]=0;//不满足条件则回溯,重新标记
}
}
}
int main(){
//freopen("input.txt","r",stdin);
while(~scanf("%d%d%d",&n,&m,&t) && (m||n||t)){
wall=0;flag=0;//初始化
memset(vis,0,sizeof(vis));//初始化
for(int i=0;i<n;i++){
scanf("%s",map+i);//接收迷宫
for(int j=0;j<m;j++){
if(map[i][j]=='S'){//标记迷宫入口
sx=i;sy=j;
vis[i][j]=1;
}
if(map[i][j]=='D'){//标记迷宫出口
dx=i;dy=j;
}
if(map[i][j]=='X')//迷宫墙计数,后边进行剪枝
wall++;
}
}
if(n*m-wall-1>=t)//路径剪枝:当可以走的总数小于所给时间,则终止
DFS(sx,sy,0);
if(flag) printf("YES\n");
else printf("NO\n");
}
return 0;
}
更详细的版本 code:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <cmath>
using namespace std;
const int MAXN = 10;
int n,m,t,sx,sy,dx,dy,wall;
char map[MAXN][MAXN];
int vis[MAXN][MAXN];
bool flag;
int abs(int x){return x>0 ? x : -x;}
void DFS(int i,int j,int c){
if(c>t) return ;//路径剪枝:超出时间则终止
if(i<0 || j<0 || i>=n || j>=m) return ;//越界判断
if(flag==true || (map[i][j]=='D' && t==c)){//满足条件则返回
flag=true;
return ;
}
int temp =abs(i-dx)+abs(j-dy);//起点到终点的步数
temp=t-temp-c;//剩余步数=时间-需要步数-已用时间时间
if(temp<0 || temp&1) return ;//奇偶剪枝:走的步数是偶数,且剩余时间大于零,才会满足条件
//map[i][j]='X';//走过就标记,防止再次访问
if(!vis[i-1][j] && map[i-1][j]!='X'){
vis[i-1][j]=true;//走过就标记,防止再次访问
DFS(i-1,j,c+1);
//if(flag) return ;
vis[i-1][j]=false;//不满足条件则回溯,重新标记
}
if(!vis[i+1][j] && map[i+1][j]!='X'){
vis[i+1][j]=true;//走过就标记,防止再次访问
DFS(i+1,j,c+1);
//if(flag) return ;
vis[i+1][j]=false;//不满足条件则回溯,重新标记
}
if(!vis[i][j-1] && map[i][j-1]!='X'){
vis[i][j-1]=true;//走过就标记,防止再次访问
DFS(i,j-1,c+1);
//if(flag) return ;
vis[i][j-1]=false;//不满足条件则回溯,重新标记
}
if(!vis[i][j+1] && map[i][j+1]!='X'){
vis[i][j+1]=true;//走过就标记,防止再次访问
DFS(i,j+1,c+1);
//if(flag) return ;
vis[i][j+1]=false;//不满足条件则回溯,重新标记
}
//map[i][j]='.';//不满足条件则回溯,重新标记
//直接对map[sx][sy]进行标记也可以,但在DFS各个方向的判断中加上if(flag) return ;
}
int main(){
while(scanf("%d%d%d",&n,&m,&t)!=EOF && (m||n||t)){
wall=0;flag=false;//初始化
memset(vis,false,sizeof(vis));//初始化
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cin>>map[i][j];//接收迷宫
if(map[i][j]=='S'){//标记迷宫入口
sx=i;sy=j;
vis[i][j]=true;
}
if(map[i][j]=='D'){//标记迷宫出口
dx=i;dy=j;
}
if(map[i][j]=='X')//迷宫墙计数,后边进行剪枝
wall++;
}
}
if(n*m-wall-1>=t)//路径剪枝:当可以走的总数小于所给时间,则终止
DFS(sx,sy,0);
if(flag) cout<<"YES"<<endl;
else cout<<"NO"<<endl;//结果输出
}
return 0;
}