学完dfs是不是感觉好像很占内存,而且非常容易超时,好像dfs就适合极小的数据计算似的,简而言之很容易炸。
当然,就简单的dfs而言确实如此,那它有没有什么优化能节约一些内存,减少运行时间呢?
(其实很多优化,在刷题过程中,不知不觉就用了,为了去掉一些不必要的情况)
先来看看这道经典题目:
来自:ZJCPC2004
Tempter of the Bone
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 169001 Accepted Submission(s): 44888
Problem Description
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
如果你没用一些剔除的条件,那么炸时间是很正常的;
!!!
写出像这样完全历遍的代码:
#include<iostream>
using namespace std;
int n,m,t,eh,el,f;
char a[8][8];
void dfs(int h,int l,int s);
int main()
{
int h,l;
while(cin>>n>>m>>t){
f=0;
if(n==0) break;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cin>>a[i][j];
if(a[i][j]=='S'){
h=i;
l=j;
}
if(a[i][j]=='D'){
eh=i;
el=j;
a[i][j]='.';
}
}
}
dfs(h,l,0);
if(f)cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}
void dfs(int h,int l,int s)
{
int i;
if(s==t){
if(h==eh&&l==el) f=1;
return;
}
if(h-1>=0&&a[h-1][l]=='.'){//这里向4个方向移动其实可以用一个循环和一个数组实现,这里是因为当时新学的dfs对数据的处理还比较死板(下面的代码也同样如此,因为懒得改);
a[h-1][l]='X';
dfs(h-1,l,s+1);
a[h-1][l]='.';
}
if(l-1>=0&&a[h][l-1]=='.'){
a[h][l-1]='X';
dfs(h,l-1,s+1);
a[h][l-1]='.';
}
if(h+1<n&&a[h+1][l]=='.'){
a[h+1][l]='X';
dfs(h+1,l,s+1);
a[h+1][l]='.';
}
if(l+1<m&&a[h][l+1]=='.'){
a[h][l+1]='X';
dfs(h,l+1,s+1);
a[h][l+1]='.';
}
return;
}
相必dfs一炸就会考虑到是不是有一些情况可以去除,因为完全不可能成立;
#include<iostream>
using namespace std;
int n,m,t,eh,el,f;
char a[8][8];
void dfs(int h,int l,int s);
int main()
{
int h,l;
while(cin>>n>>m>>t){
f=0;
if(n==0) break;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cin>>a[i][j];
if(a[i][j]=='S'){
h=i;
l=j;
}
if(a[i][j]=='D'){
eh=i;
el=j;
a[i][j]='.';
}
}
}
if(((((h+l)%2)+((eh+el)%2)))!=(t%2))cout<<"NO"<<endl;//增加部分
else{
dfs(h,l,0);
if(f)cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
}
return 0;
}
void dfs(int h,int l,int s)
{
int i;
if(s==t){
if(h==eh&&l==el) f=1;
return;
}
if((eh-h)+(el-l)+s>t)return;//增加部分(这里其实没有也可以过)
if(h-1>=0&&a[h-1][l]=='.'){
a[h-1][l]='X';
dfs(h-1,l,s+1);
a[h-1][l]='.';
}
if(l-1>=0&&a[h][l-1]=='.'){
a[h][l-1]='X';
dfs(h,l-1,s+1);
a[h][l-1]='.';
}
if(h+1<n&&a[h+1][l]=='.'){
a[h+1][l]='X';
dfs(h+1,l,s+1);
a[h+1][l]='.';
}
if(l+1<m&&a[h][l+1]=='.'){
a[h][l+1]='X';
dfs(h,l+1,s+1);
a[h][l+1]='.';
}
return;
}
然后就发现ac了,这里用到了两个折枝方法
1:前面那串增加代码(奇偶折枝)
这是什么意思?
先看
1 0 1 0
0 1 0 1
1 0 1 0
0 1 0 1
你可以得到从1-1或者0-0所走的步数必然是偶数;
而1-0或0-1必然是奇数;
所以直接计算起点-终点和能走的时间,如果发现不对应,直接砍掉大枝条,输出no;
当然也可以这样:如果,(T-step-s)是奇数,则剪掉(奇偶剪枝)。step为当前步数,s为最短距离,T为时间;
2:如果T < s+step,则剪掉,即为第二段增加代码
3:(路径剪枝)
如果地图中,可走的点的数目(xnum) < 要求的时间T,则剪掉
这个实现起来比较复杂~~(我比较懒)~~ ,所以没有打上去,就这题而言其实用处不是很大。