AcWing
  • 首页
  • 课程
  • 题库
  • 更多
    • 竞赛
    • 题解
    • 分享
    • 问答
    • 应用
    • 校园
  • 关闭
    历史记录
    清除记录
    猜你想搜
    AcWing热点
  • App
  • 登录/注册

AcWing 1015. 摘花生    原题链接    简单

作者: 作者的头像   一只野生彩色铅笔 ,  2021-05-25 15:55:31 ,  所有人可见 ,  阅读 4130


43


6

最近在补全提高课所有题目的题解,引荐一下汇总的地方提高课题解汇总

题目描述

给定一个 $n \times m$ 的矩阵,起点位置在 $(1,1)$,终点位置是 $(n, m)$

接着给定该矩阵上,每个位置$(x,y)$上的物品的价值$w$

现需要我们制定一个方案:

从起点出发,只能向右或向下走,如何走到终点,才能使经过的所有格子的物品总价值最大

题解

这题是一道标准的动态规划问题,模型是数字三角形

我们先来分析一下

如果要进行动态规划,则用来表示当前状态的参数有哪些?

  1. 当前走到第$i$行
  2. 当前走到第$j$列

于是乎,我们可以开二维数组f[i][j]来存储当前在$i$行$j$列的状态

而f[i][j]的值,就是表示从起点走到该点经过的所有格子的价值总和的最大值

则最终答案的状态就是f[n][m]

然后再考虑状态转移方程

因为限制了只能向右或向下走,因此当前状态f[i][j]只能由f[i-1][j]或f[i][j-1]转移过来

闫氏DP分析法

$$ \begin{cases} 状态表示\mathrm{f(i,j)} \begin{cases} 属性: 从起点出发,走到第\mathrm{i}行第\mathrm{j}列的所有方案 \\\ 集合: 方案中的路线经过的所有物品的总价值最大 Max \end{cases} \\\ 状态转移 f(i,j) = max\{f(i - 1, j), f(i, j - 1)\} + w(i,j) \end{cases} $$

集合划分

IMG_F6B52EA4DC23-1.jpeg

Code(循环写法)

#include <iostream>
#include <cstring>

using namespace std;

const int N = 110;

int T;
int n, m;
int w[N][N];
int f[N][N];

int main()
{
    cin >> T;
    while (T -- )
    {
        memset(w, 0, sizeof w);
        memset(f, 0, sizeof f);
        cin >> n >> m;

        for (int i = 1; i <= n; ++ i)
        {
            for (int j = 1; j <= m; ++ j)
            {
                cin >> w[i][j];
            }
        }

        //dp
        for (int i = 1; i <= n; ++ i)
        {
            for (int j = 1; j <= m; ++ j)
            {
                f[i][j] = max(f[i][j - 1], f[i - 1][j]) + w[i][j];
            }
        }

        cout << f[n][m] << endl;
    }
    return 0;
}

Code(记忆化搜索写法)

#include <iostream>
#include <cstring>

using namespace std;

const int N = 110, INF = 0x3f3f3f3f;

int T;
int n, m;
int w[N][N];
int f[N][N];

int dp(int x, int y)
{
    if (f[x][y] >= 0) return f[x][y];
    if (x < 1 || x > n || y < 1 || y > m) return f[x][y] = -INF;
    if (x == 1 && y == 1) return f[x][y] = w[x][y];

    int t = 0;
    t = max(t, dp(x - 1, y) + w[x][y]);
    t = max(t, dp(x, y - 1) + w[x][y]);
    return f[x][y] = t;
}
int main()
{
    cin >> T;
    while (T -- )
    {
        memset(w, 0, sizeof w);
        memset(f, -1, sizeof f);
        cin >> n >> m;

        for (int i = 1; i <= n; ++ i)
        {
            for (int j = 1; j <= m; ++ j)
            {
                cin >> w[i][j];
            }
        }
        cout << dp(n, m) << endl;
    }
    return 0;
}

10 评论


用户头像
Verse   2022-05-03 18:21         踩      回复

学会了记忆化搜索,感谢&&膜拜大佬。

#include <bits/stdc++.h>
using namespace std;
const int N =105;
int rec[N][N],f[N][N],t,c,r;

int dp(int x,int y){
    if(x < 1 || y < 1 )return 0;
    if(f[x][y] > 0)return f[x][y];
    return f[x][y] =  max(dp(x-1,y),dp(x,y-1))+rec[x][y];
}

int main(){
    cin >> t;
    while(t--){
        cin >> r >> c;
        memset(f,-1,sizeof(f));
        memset(rec,0,sizeof(rec));
        for(int i = 1; i <= r; ++i){
            for(int j = 1; j <= c; ++j){
                cin >> rec[i][j];
            }
        }
        cout << dp(r,c) << endl;
    }
    return 0;
}
用户头像
ToThink   2023-01-20 14:05         踩      回复

大佬,请问下,为什么只能倒着搜呢,从(r, c)—>(1,1)


用户头像
包子云   2022-03-06 16:07         踩      回复

记忆化搜索运行时间甚至比不用更长..代码量多了那这还有必要吗?话说用了不应该时间短些的吗?

用户头像
威子   2022-03-08 20:42         踩      回复

这个可能是数据量的问题吧,如果数据多时间复杂度的排序应该是DP(本题可以做的话) > 记忆化 > 暴力搜索。

用户头像
残阳如血   2024-02-17 15:59      1    踩      回复

记搜本来就慢啊,但是好理解

用户头像
zhoumurui   2024-08-28 15:19         踩      回复

记忆化搜索本来就慢


用户头像
Meliordas   2021-10-31 15:29         踩      回复

请问是不是如果都从0开始枚举会数组越界啊?而且为什么这道题不用初始化f[1][1]呢?

用户头像
一只野生彩色铅笔   2021-11-05 23:18      1    踩      回复

状态转移中有 $i - 1$,如果从 $0$ 开始要特判

初始化看初始状态的要求

用户头像
糖豆   2022-07-05 20:37      1    踩      回复

动态规划,初始递推值的设置是第一个环节,有时这个环节也可以省略,比如本题。我们来看下递推关系式:

 f[i][j] = max(f[i - 1][j], f[i][j - 1]) + w[i][j];

当i=1,j=1时:

f[1][1]=max(f[0][1],f[1][0])+w[1][1];

因为我们设计的数组是从$1$行$1$列开始的,$0$行$0$列被留空了,默认值是$0$,所以f[0][1]=0,f[1][0]=0,执行的结果就是f[1][1]=w[1][1],也就是捎带着完成了递推值的初始化,当然,我们也可以手动对起始值进行手动初始化,然后再通过循环完成后续的递推,也是一样的。不同的题目,可能递推值就无法通过循环完成初始化,这时就需要我们手动完成初始化操作。

用户头像
小马云写代码   2023-03-25 19:55    回复了 糖豆 的评论         踩      回复

感谢


App 内打开
你确定删除吗?
1024
x

© 2018-2025 AcWing 版权所有  |  京ICP备2021015969号-2
用户协议  |  隐私政策  |  常见问题  |  联系我们
AcWing
请输入登录信息
更多登录方式: 微信图标 qq图标 qq图标
请输入绑定的邮箱地址
请输入注册信息