一看就知道是神搜,但是弄了好长时间一直是TimeLimited,开始的时候有剪枝的意识,但是全是小修小补,包括搜索到符合条件的及时回溯等方式,都还是不行,查资料才知道还有奇偶剪枝这回事,感觉很强大
关于奇偶剪枝:
应用范围:
给定你起点S,和终点D,之间有障碍,判断是否能在 T 时刻恰好到达终点D(下面的例题就是经典)
原理解释:
是数据结构的搜索中,剪枝的一种特殊小技巧。
现假设起点为(sx,sy),终点为(ex,ey),给定t步恰好走到终点,
| s | ||||
| | | ||||
| | | ||||
| | | ||||
| + | — | — | — | e |
如图所示(“|”竖走,“—”横走,“+”转弯),易证abs(ex-sx)+abs(ey-sy)为此问题类中任意情况下,起点到终点的最短步数,记做step,此处step1=8;
| s | — | — | — | |
| — | — | + | ||
| | | + | |||
| | | ||||
| + | — | — | — | e |
如图,为一般情况下非最短路径的任意走法举例,step2=14;
step2-step1=6,偏移路径为6,偶数(易证);
故,若t-[abs(ex-sx)+abs(ey-sy)]结果为非偶数(奇数),则无法在t步恰好到达;
返回,false;
反之亦反。
典型应用:题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=1010
本题思路:深度搜索,对上下左右四个方向进行深搜,深搜前注意剪枝就好了...
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;
int sum=1, row,line,doorrow,doorline,ttime,mark=0;//mark为是否可行的标记
char ch[10][10]={0};
void find(int i,int j){
if(mark==0){
if(sum==ttime&&doorrow==i&&doorline==j){
mark=1;
}
else if(sum<ttime&&mark==0){
if(i>0&&(ch[i-1][j]=='.'||ch[i-1][j]=='D')){//向上深搜
// cout<<"上移"<<" i:"<<i-1<<' '<<"j:"<<j<<" sum:"<<sum+1<<" time "<<time<<endl;
sum++;
ch[i-1][j]='X';
find(i-1,j);
ch[i-1][j]='.';//还原
sum--; //还原
}
if(mark==1)
return ;
if(i<row-1&&(ch[i+1][j]=='.'||ch[i+1][j]=='D')){//向下深搜
ch[i+1][j]='X';
sum++;
find(i+1,j);
ch[i+1][j]='.';
sum--;
}
if(mark==1)
return ;
if(j>0&&(ch[i][j-1]=='.'||ch[i][j-1]=='D')){////向左深搜
ch[i][j-1]='X';
sum++;
find(i,j-1);
ch[i][j-1]='.';
sum--;
}
if(mark==1)
return ;
if(j<line-1>0&&(ch[i][j+1]=='.'||ch[i][j+1]=='D')){//向右深搜
ch[i][j+1]='X';
sum++;
find(i,j+1);
ch[i][j+1]='.';
sum--;
}
}
}
}
int main (){
int startrow,startline;
while(~scanf("%d%d%d",&row,&line,&ttime),line,row,ttime){
sum=0;
mark=0;
for(int i=0;i<row;i++){
scanf("%s",ch[i]);
}
for(int i=0;i<row;i++) {
for(int j=0;j<line;j++){
if(ch[i][j]=='D')
doorrow=i,doorline=j;
if(ch[i][j]=='S')
startrow=i,startline=j;
}
}
if( (ttime-(abs(doorrow-startrow)+abs(doorline-startline)))%2!=0)//奇偶剪枝
printf("NO
");
else{
find(startrow,startline);
printf("%s
",(mark==0?"NO":"YES"));
}
}
return 0;
}本来感觉下面的在每一次深搜时都剪枝,应该比上面只在第一次的时候剪枝要优化,效率更高,但是提交时却时间更长,感觉应该是测试数据小的原因
第一种时间:
第二种时间:
代码:
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;
int sum=1, row,line,doorrow,doorline,ttime,mark=0,startrow,startline;//mark为是否可行的标记
char ch[10][10]={0};
void find(int i,int j){
if(mark==1) return;
if( (ttime-(abs(doorrow-startrow)+abs(doorline-startline)))%2!=0)//奇偶剪枝,且每次进入都剪枝
return ;
if(sum==ttime&&doorrow==i&&doorline==j){
mark=1;
}
else if(sum<ttime&&mark==0){
if(i>0&&(ch[i-1][j]=='.'||ch[i-1][j]=='D')){
sum++;
ch[i-1][j]='X';
find(i-1,j);
ch[i-1][j]='.';
sum--;
}
if(mark==1)
return ;
if(i<row-1&&(ch[i+1][j]=='.'||ch[i+1][j]=='D')){
ch[i+1][j]='X';
sum++;
find(i+1,j);
ch[i+1][j]='.';
sum--;
}
if(mark==1)
return ;
if(j>0&&(ch[i][j-1]=='.'||ch[i][j-1]=='D')){
ch[i][j-1]='X';
sum++;
find(i,j-1);
ch[i][j-1]='.';
sum--;
}
if(mark==1)
return ;
if(j<line-1>0&&(ch[i][j+1]=='.'||ch[i][j+1]=='D')){
ch[i][j+1]='X';
sum++;
find(i,j+1);
ch[i][j+1]='.';
sum--;
}
if(mark==1)
return ;
}
}
int main (){
while(~scanf("%d%d%d",&row,&line,&ttime),line,row,ttime){
sum=0,mark=0; //别忘了每次都要初始化
for(int i=0;i<row;i++)
scanf("%s",ch[i]);
for(int i=0;i<row;i++)
for(int j=0;j<line;j++){
if(ch[i][j]=='D')
doorrow=i,doorline=j;
if(ch[i][j]=='S')
startrow=i,startline=j;
}
find(startrow,startline);
printf("%s
",(mark==0?"NO":"YES"));
}
return 0;
}