hdu 1078 记忆化搜索

hdu 1078 http://acm.hdu.edu.cn/showproblem.php?pid=1078
题意:每次可以朝一个方向走k格,必须走到一个数值比当前值大的点,问最多数值加起来能有多少,走到不能再走。
一开始定义错了,定义dp[i][j] 为以(i,j)为终点时所能得到的最大值。
dp[i+t*dx][j+t*dy] = max(dp[i+t*dx][j+t*dy],dp[i][j] + a[i+t*dx][j+t*dy]) (1<=t<=k)
这样TLE了很久,因为会重复更新很多东西,有的格子还不能保证这时所得到的是这个格子所能得到的最大的时候就去更新别的格子了,此时这个工作就是完全没有用的。
比如样例中(0,0) -> (0,1) ->(1,1) 此时(1,1)不是所能达到的最大,用(1,1)去更新了(2,1) ,但是(0,0) -> (0,1) ->(0,2)->(1,2) -> (1,1) 此时这个(1,1)才是所能达到的最大。 这时拿去更新(2,1,)才能得到最大。

感觉这种跟路径有关的问题多是用dfs搞的,跟dp有关的,还经常要用到记忆化搜索,自己很不敏感,果然还是dp练得太少了。。
修改定义:dp[i][j] 为以(i,j)为起点时所能得到的最大值。
**转移方程:**dp[i][j] = max(dp[i+t*dx][j+t*dy]) (1<=t<=k) + a[i][j]
分解子问题:
在求max里的时候又可以将(i+t*dx,j+t*dy)当做起点来找dp[i+t*dx][j+t*dy]这样就可以下去递归找了。
这样能够保证一旦每个格子算出结果就是所能获得的最大值,就能运用记忆化搜索来搞了。如果这个dp[i][j] 有值,那他就是所能获得的最大值,直接返回dp[i][j]。
错误定义的代码:

int dp[M][M]; //以(i,j)为终点的所能拿到的最大值
void dfs(int x,int y)
{
    for(int i = 0; i < 4;i++)
    {
        for(int j = 1;j <= k;j++)
        {
            int nx = x + j*dx[i];
            int ny = y + j*dy[i];
            if(nx < 0 || ny < 0 || nx >= n || ny >= n) continue;
            if(a[nx][ny] > a[x][y])
            {
                dp[nx][ny] = max(dp[nx][ny],dp[x][y] + a[nx][ny]);
                ans = max(dp[nx][ny],ans);
                dfs(nx,ny);
            }
        }
    }
}

正确定义后:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
#define M 109
int a[M][M],dp[M][M]; //以(i,j)为起点的最多能拿到几个
int dx[4] = {0,1,0,-1};
int dy[4] = {1,0,-1,0};
int n,k;
int ans;
int dfs(int x,int y)//返回以(x,y)为起点时最多能有几个
{
    if(dp[x][y] > 0) return dp[x][y]; //如果已经存在值了则返回
    int maxx = 0;
    for(int i = 0; i < 4;i++)
    {
        for(int j = 1;j <= k;j++)
        {
            int nx = x + j*dx[i];
            int ny = y + j*dy[i];
            if(nx < 0 || ny < 0 || nx >= n || ny >= n) continue;
            if(a[nx][ny] > a[x][y]) //可以从(x,y)走到(nx,ny)
            {
                maxx = max(maxx,dfs(nx,ny)); //再将(nx,ny)当做起点找dp[nx][ny]
            }
        }
    }
    //记忆化搜索
    return dp[x][y] = a[x][y] + maxx; //用自身的数加上最大的能转移来的更新dp[i][j]
}
int main()
{
    while(scanf("%d %d",&n,&k) == 2)
    {
        if(n == -1 && k == -1) break;
        for(int i = 0;i < n;i++)
        {
            for(int j = 0;j < n;j++) scanf("%d",&a[i][j]);
        }
        memset(dp,0,sizeof(dp));
        printf("%d\n",dfs(0,0));
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值