题解 UVA10285 【最长的滑雪路径 Longest Run on a Snowboard】
posted on 2021-03-01 18:05:25 | under 题解 | source
此题正解是记忆化搜索,我们先考虑暴力 dfs。
显然,我们可以在 \(\operatorname{dfs}\) 中传 \(3\) 个参数:\(x,y,cnt\),分别表示当前的位置和滑雪路径的长度。每次更新答案,并往另外 \(4\) 个点继续搜。
void dfs(int x,int y,int cnt){
ans=Max(ans,cnt);
for(int i=1;i<=4;i++){
int tmpx=x+dx[i],tmpy=y+dy[i];
if(tmpx<1||n<tmpx||tmpy<1||m<tmpy) continue;
if(a[tmpx][tmpy]>=a[x][y]) continue;
//我们可以不标记走过的点,因为你不可能滑下来又滑上去
dfs(tmpx,tmpy,cnt+1);
}
}
这种方法是正确的,但时间复杂度达到了 \(O(4^{nm})\),会 TLE,考虑优化。
不难发现,从一个点出发的最长滑雪路径的长度是一定的,搜过一次,下次再搜,会浪费很多时间。我们可以把这个最长滑雪路径的长度用一个数组 \(f\) 存起来,使用 \(f_{x,y}\) 表示 \((x,y)\) 从这个点出发的最长滑雪路径,如果搜过了,\(f_{x,y}\) 会有一个值,这时候就不用继续搜索下去了,直接返回 \(f_{x,y}\)。
为了方便,我们可以让 \(f\) 数组初始化为 \(-1\),判断 \(f_{x,y}\) 有没有值时可以写成if(~f[x][y])
,这样能避免"从这个点出发的最长滑雪路径的长度为 \(0\) 而继续搜"的情况。
这就是记忆化搜索的思想,经过记忆化,时间复杂度从 \(O(4^{nm})\) 优化成了 \(O(nm)\)。
下面是代码:
#include <cstdio>
#include <cstring>
using namespace std;
template<class T>
T Max(T _,T __){return _<__?__:_;}
template<class T>
T Min(T _,T __){return _>__?__:_;}
const int dx[5]={0,-1,0,0,1},
dy[5]={0,0,-1,1,0};//四个方向打表
int n,m,a[110][110],ans;
int f[110][110];
int dfs(int x,int y,int cnt){//注意记忆化的 dfs 有返回值
if(~f[x][y]) return f[x][y];//记忆化
int maxcnt=0;//记录最长滑雪路径的长度
for(int i=1;i<=4;i++){
int tmpx=x+dx[i],tmpy=y+dy[i];
if(tmpx<1||n<tmpx||tmpy<1||m<tmpy) continue;
if(a[tmpx][tmpy]>=a[x][y]) continue;
maxcnt=Max(maxcnt,dfs(tmpx,tmpy,cnt+1)+1);
//最长滑雪路径包括这个点本身,要 +1
}
return f[x][y]=maxcnt;//赋值语句有返回值
}
int mian(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
ans=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
ans=Max(ans,dfs(i,j,1));
//注意,最长滑雪路径不一定从最高的点开始,要每个点搜一次
return 0;
}
char ipt[100010];
int main(int T){//其实可以偷 main 的参数的
for(scanf("%d",&T)/*偷过来用*/;T--;puts("")){
memset(a,0,sizeof a);
memset(f,-1,sizeof f);//以上为多测日常操作
scanf("%s",ipt);
mian();
printf("%s: %d",ipt,ans+1);//因为一些玄学问题 ans 要 +1
}
return 0;
}
另外,此题与P1434 [SHOI2002]滑雪是双倍经验。
本文来自博客园,作者:caijianhong,转载请注明原文链接:https://www.cnblogs.com/caijianhong/p/solution-UVA10285.html