P1434_#include 简单搜索
滑雪

1. 题目大意:

给出一个n*m的区域,求从任意一点出发滑向上下左右单调递减的最大路径。

2. 普通dfs怎么做:

从每一个点开始dfs,取最大值。
用两个数组dx,dy表示上下左右偏移量:

int dx[4]={0,-1,0,1},dy[4]={1,0,-1,0};

可以继续走的条件((ux,uy)表示现在的坐标,(nx,ny)表示下一步的坐标):

nx>0&&nx<=n&&ny>0&&ny<=n //边界
maps[nx][ny]<maps[ux][uy] //保持单调递减

没有优化的dfs代码:

int dfs(int ux,int uy){
    int a=1;
    for(int i=0;i<4;i++){ //四个方向
        int nx=ux+dx[i];
        int ny=uy+dy[i];
        if(nx>0&&nx<=n&&ny>0&&ny<=n&&maps[nx][ny]<maps[ux][uy])
            a=max(a,dfs(nx,ny)+1); //符合条件,继续向下走取最大值
    }
    return a;
}

3. 记忆化搜索:

暴搜很显然会TLE,那么如何优化呢?
看下面一个数据:

2 3
1 3 3
2 3 2

(1,1)的最大距离是1:(1,1)
(1,2)的最大距离是2:(1,2)->(1,1)
(1,3)的最大距离是2:(1,3)->(2,3)
(2,1)的最大距离是2:(2,1)->(1,1)
(2,2)的最大距离是3:(2,2)->(2,1)->(1,1)
(2,3)的最大距离是1:(2,3)
所以最终的结果是3。
当我们dfs到(2,2)这个点时,他的下一个点是(2,1)或(2,3)。
(2,1)这个点的最大距离我们之前已经计算过了,那为什么还要计算一遍呢?
所以我们可以用一个数组dp记录dfs时每个点的最大距离,方便下一次dfs直接调用。
具体如何用请看代码。
优化后的dfs:

int dfs(int ux,int uy){ //记忆化搜索
    if(dp[ux][uy]) return dp[ux][uy]; //如果之前计算过了,直接调用计算过的值
    int a=1;
    for(int i=0;i<4;i++){
        int nx=ux+dx[i];
        int ny=uy+dy[i];
        if(nx>0&&nx<=n&&ny>0&&ny<=n&&maps[nx][ny]<maps[ux][uy])
            a=max(a,dfs(nx,ny)+1); //符合条件继续向下滑
    }
    dp[ux][uy]=a; //记录下最大值
    return a;
}

4. 完整代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cctype>
using namespace std;
inline int read(){ //快读
    int s=0,w=0;
    char ch=0;
    while(!isdigit(ch)){
        w|=ch=='-';
        ch=getchar();
    }
    while(isdigit(ch)){
        s=(s<<3)+(s<<1)+(ch^48);
        ch=getchar();
    }
    return w?-s:s;
}
inline void write(int x){
    if(x<0) putchar('-'),x=-x;
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
int n,m,maps[105][105],ans=0,dp[105][105]; 
int dx[4]={0,-1,0,1},dy[4]={1,0,-1,0}; //四个方向
int dfs(int ux,int uy){ //记忆化搜索
    if(dp[ux][uy]) return dp[ux][uy]; //如果之前计算过了,直接调用计算过的值
    int a=1;
    for(int i=0;i<4;i++){
        int nx=ux+dx[i];
        int ny=uy+dy[i];
        if(nx>0&&nx<=n&&ny>0&&ny<=n&&maps[nx][ny]<maps[ux][uy])
            a=max(a,dfs(nx,ny)+1); //符合条件继续向下滑
    }
    dp[ux][uy]=a; //记录下最大值
    return a;
}
int main(){
    n=read(),m=read();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            maps[i][j]=read();
    for(int i=1;i<=n;i++) //从每一个点开始dfs
        for(int j=1;j<=m;j++)
            ans=max(ans,dfs(i,j));
    write(ans);
    return 0;
}