最长滑雪道(递归 dfs dp)

问题描述

滑雪区域由一个二维数组给出。数组的每个数字代表点的高度。如下:数组的每个数字代表点的高度
一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。在上面的例子中,一条可滑行的滑坡为24-17-16-1。当然25-24-23-…-3-2-1更长。事实上,这是最长的一条。

你的任务就是找到最长的一条滑坡,并且将滑坡的长度输出。 滑坡的长度定义为经过点的个数,例如滑坡24-17-16-1的长度是4。

递归+dfs思想:
首先一个点要向上下左右四个方向移动,就可以定义两个一维数组(也可以是一个二维数组)来组成这四个方向:

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

然后一个点往一个方向移动到下一个点(下一个点满足比上一个点高度低),则又遍历到下一个点,相应的滑坡长度也要加一;如果下一个点无论哪个方向都不满足,则退回到上一个点,另选一个方向,相应的滑坡长度也要减一,就可以用递归实现:

if(x>=0 && x<R && y>=0 && y<C && d[x][y]<d[i][j])
  {
   sum++;
   dfs(d,x,y,sum);
   sum--;
  }

AC代码如下:

#include <iostream>
using namespace std;
int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1};
int c=0;//定义一个总长度
int R,C;//定义行数、列数
void dfs(int d[][105],int i,int j,int sum)
{
 if(sum>c)//sum用于递归的时候改变c的值
  c=sum;
 for(int t=0;t<4;t++)
 {
  int x=i+dx[t],y=j+dy[t];//x、y是同时改变的 对应四个方向
  if(x>=0 && x<R && y>=0 && y<C && d[x][y]<d[i][j])
  {
   sum++;
   dfs(d,x,y,sum);
   sum--;//直到上面dfs函数里面for循环完没有一个满足时,sum--并且回到上一个点寻找另一个满足的方向
  }
 }
}
int main()
{
 int d[105][105];
 cin>>R>>C;
 for(int i=0;i<R;i++)
  for(int j=0;j<C;j++)
   cin>>d[i][j];
 for(int i=0;i<R;i++)
  for(int j=0;j<C;j++)
   dfs(d,i,j,1);//从d[0][0]这个点一直遍历到d[R-1][C-1]点
 cout<<c<<endl;
 return 0;
}

这道题用上述方法可能就会存在比如有三个点A、B、C,A点遍历到不满足的B点,然后C点遍历到满足的A点,A点已经知道B点不满足,但是用上述方法,A点有可能又要判断一次B点满不满足,没有记忆功能,加重代码运算量。所以这时我们可以改善代码,用dp+dfs思想。
dp+dfs思想:
新增一个二维数组用于记忆功能并初始化里面数字都为-1:

int dp[105][105];

其他思想差不多,只是在dfs函数里增加一个if判断返回语句,减少代码的运算量:

if(dp[r][c]!=-1)
  return dp[r][c];

AC代码如下:

#include<iostream>
#include<algorithm>
using namespace std;

int d[105][105];
int dp[105][105];
int R,C;//定义行数、列数
int dxy[4][2] = {{0,1},{1,0},{0,-1},{-1,0}};//上面用的一维数组,这里定义一个二维数组

int dfs(int r,int c)
{
 int x,y;
 if(dp[r][c]!=-1) //之前到过这个点处
  return dp[r][c];//存放的其实就是这个点能遍历的最长长度
 dp[r][c]=1;//从这点开始计算长度
 for (int i = 0; i < 4; i++)
 {
  x = r + dxy[i][0];
  y = c + dxy[i][1];
  if (x >= 0 && x < R && y >= 0 && y < C && d[r][c] > d[x][y])
   dp[r][c]=max(dp[r][c],dfs(x,y)+1);//调用algorithm库里面的max函数,取这个点能遍历的最长长度
 }
 return dp[r][c];
}

int main()
{
 int L=0;
 cin >> R >> C;
 for (int i = 0; i < R; i++)
  for (int j = 0; j < C; j++)
  {
   cin >> d[i][j];
   dp[i][j]=-1;//初始化d[][]数组的同时,将dp数组数值全赋值为-1
  }
 for (int i = 0; i < R; i++) 
  for (int j = 0; j < C; j++)
   L=max(L,dfs(i,j)); //与上述算法同样遍历,但你会发现从dfs(0,1)开始,for里面的dfs()函数只执行一次了,因为上一个点对应的dp[][]已经不等于-1
 cout << L << endl;
 return 0;
}

如果还是有点迷惑,你可以在主函数中调用输出dp数组的代码:

 for (int i = 0; i < R; i++)
 {
  for (int j = 0; j < C; j++)
  {
   cout<<dp[i][j]<<" ";
  }
  cout<<endl;
 }

相信你就会明白的!!!

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值