题目大意

滑雪
Michael喜欢滑雪百这并不奇怪, 因为滑雪的确很刺激。可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你。Michael想知道载一个区域中最长底滑坡。区域由一个二维数组给出。数组的每个数字代表点的高度。下面是一个例子

1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。在上面的例子中,一条可滑行的滑坡为24-17-16-1。当然25-24-23-…-3-2-1更长。事实上,这是最长的一条。

输入格式

输入的第一行表示区域的行数R和列数C(1 <= R,C <= 100)。下面是R行,每行有C个整数,代表高度h,0<=h<=10000。

输出格式

输出最长区域的长度。

样例输入

5 5
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9

样例输出

25

解题思路

记忆化搜索

将算过的每一个f[i][j]记录下来,再一次需要算f[i][j]时不再递归直接用记录下来的结果

AC代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=307;
int a[maxn][maxn],f[maxn][maxn];
int r,c,ans;
int dp(int x,int y)
{
    if(f[x][y]) return f[x][y];  //保证每个子问题只读了一遍
    f[x][y]=1;
    if(x>1 && (a[x-1][y]<a[x][y])) f[x][y]=max(f[x][y],dp(x-1,y)+1); //左
    if(x<r && (a[x+1][y]<a[x][y])) f[x][y]=max(f[x][y],dp(x+1,y)+1);  //右
    if(y>1 && (a[x][y-1]<a[x][y])) f[x][y]=max(f[x][y],dp(x,y-1)+1);  //上
    if(y<c && (a[x][y+1]<a[x][y])) f[x][y]=max(f[x][y],dp(x,y+1)+1);  //下
    return f[x][y];
}
int main()
{
    memset(f,0,sizeof f);
    scanf(" %d%d",&r,&c);
    for(int i=1;i<=r;++i)
        for(int j=1;j<=c;++j)
            scanf(" %d",&a[i][j]);

    for(int i=1;i<=r;++i)
        for(int j=1;j<=c;++j)
            ans=max(ans,dp(i,j));
    printf("%d\n",ans);
    return 0;
}

记忆化搜索

用数组将已经算过的东西记录下来,在下一次使用时直接用已经算出的值,避免重复运算,去掉重复的搜索树。

斐波那契数列 f(n)

f[n]=1 if(n==0 || n==1)

f[n]=f[n-1]+f[n-2] if(n>1)

递归版

int f(int n)
{
    if(n==0 || n==1) return 1;
    else return f(n-1)+f(n-2);
}

递推版

a[0]=a[1]=1;
for(int i=2;i<=n;++i) a[i]=a[i-1]+a[i-2];
return a[n];

记忆化搜索

时间复杂度O(n)

int cal(int n)
{
    if(f[n]) return f[n];
    return (f[n]=cal(n-1)+cal(n-2))
}

记忆化搜索相当于搜索中的剪枝,避免重复计算已经出现过的子树,效率比递推和递归版本快的多。


一个爱折腾且喜欢研究算法的大学生