首页 > 编程语言 >​Leetcode 166.珠宝的最高价值​ 网格图dp C++实现

​Leetcode 166.珠宝的最高价值​ 网格图dp C++实现

时间:2024-10-31 09:48:36浏览次数:3  
标签:return int frame C++ dfs 166 Leetcode dp size

问题:Leetcode 166.珠宝的最高价值

现有一个记作二维矩阵 frame 的珠宝架,其中 frame[i][j] 为该位置珠宝的价值。拿取珠宝的规则为:

  • 只能从架子的左上角开始拿珠宝
  • 每次可以移动到右侧或下侧的相邻位置
  • 到达珠宝架子的右下角时,停止拿取

注意:珠宝的价值都是大于 0 的。除非这个架子上没有任何珠宝,比如 frame = [[0]]

a7d8bff1eb824f2280b172582fc9c154.png


算法1:递归 寻找子问题(超时)

ce9a52370d124b79b4fc0c84d91d8461.png

受上图启发,定义 dfs ( i , j ) 表示从左上角到第 i 行第 j 列这个格子(记作  ( i , j ) )的最大价值和。

分类讨论怎么到达 ( i , j )

如果是从左边过来,则 dfs ( i , j ) = dfs ( i , j − 1 ) + frame [ i ] [ j ] 
如果是从上边过来,则 dfs ( i , j ) = dfs ( i − 1 , j ) + frame [ i ] [ j ] 

这两取最大值,得 dfs ( i , j ) = max ( dfs ( i , j − 1 ) , dfs ( i − 1 , j ) ) + frame [ i ] [ j ]
递归边界:当 i < 0 j < 0 时,返回 0,因为出界是没有价值的。也就是 dfs ( −1 , j ) = 0 , dfs ( i , − 1 ) = 0 

递归入口:dfs(m−1,n−1)

代码:

class Solution {
public:
    int jewelleryValue(vector<vector<int>>& frame) {
        function<int(int,int)>dfs = [&](int i,int j)->int{
            if(i < 0 || j < 0)  return 0;
            return max(dfs(i,j-1),dfs(i-1,j)) + frame[i][j];
        };
        return dfs(frame.size() - 1,frame[0].size() - 1);
    }
};

算法2:记忆化搜索

如果一个状态(递归入参)是第一次遇到,那么可以在返回前,把状态及其结果记到一个 memo 数组(或哈希表)中;
如果一个状态不是第一次遇到,那么直接返回 memo 中保存的结果

代码:

class Solution {
public:
    int jewelleryValue(vector<vector<int>>& frame) {
        int m = frame.size(),n = frame[0].size();
        int memo[m][n]; // memo数组 存放记录
        memset(memo, 0, sizeof(memo)); // 初始化
        function<int(int,int)>dfs = [&](int i,int j)->int{
            if(i < 0 || j < 0)  return 0;
            int &res = memo[i][j];
            if(res) return res;
            return res = max(dfs(i,j-1),dfs(i-1,j)) + frame[i][j];
        };
        return dfs(m - 1,n - 1);
    }
};

 


算法31:1 翻译成递推

代码:

class Solution {
public:
    int jewelleryValue(vector<vector<int>>& frame) {
        int m = frame.size(),n = frame[0].size();
        int dp[m + 1][n + 1];
        memset(dp, 0, sizeof(dp));
        for(int i = 0;i < m;i++)
            for(int j = 0;j < n;j++)
                dp[i + 1][j + 1] = max(dp[i + 1][j],dp[i][j + 1]) + frame[i][j];

        return dp[m][n];
    }
};

 


算法4:空间优化·方法一(两个数组,滚动数组)

由于 dp [ i + 1 ] 只依赖 dp [ i ] ,那么 dp [ i − 1 ] 及其之前的数据就没用了。

例如计算 dp [ 2 ] 的时候,数组 dp [ 0 ] 不再使用了。

那么干脆把 dp [ 2 ] 填到 dp [ 0 ] 中。同理,把 dp [ 3 ] 填到 dp [ 1 ] 中,dp [ 4 ] 填到 dp [ 0 ] 中,……

因此可以只用两个长为 n+1 的数组滚动计算。

代码:

class Solution {
public:
    int jewelleryValue(vector<vector<int>>& frame) {
        int m = frame.size(),n = frame[0].size();
        int dp[2][n + 1];
        memset(dp, 0, sizeof(dp));
        for(int i = 0;i < m;i++)
            for(int j = 0;j < n;j++)
                dp[(i + 1) % 2][j + 1] = max(dp[(i + 1) % 2][j],dp[i % 2][j + 1]) + frame[i][j];

        return dp[m % 2][n];
    }
};

算法5:空间优化·方法二(一个数组)

举个例子,在计算 dp [ 1 ] [ 1 ] 时,会用到 dp [ 0 ] [ 1 ] ,但是之后就不再用到了。那么干脆把 dp [ 1 ] [ 1 ] 记到 dp [ 0 ] [ 1 ] 中,这样对于 dp [ 1 ] [ 2 ] 来说,它需要的数据就在 dp [ 0 ] [ 1 ]dp [ 0 ] [ 2 ] 中。dp [ 1 ] [ 2 ] 算完后也可以同样记到 dp [ 0 ] [ 2 ] 中。

所以只需要一个长为 n+1 的一维数组就够了。

代码1:

class Solution {
public:
    int jewelleryValue(vector<vector<int>>& frame) {
        int m = frame.size(),n = frame[0].size();
        int dp[n + 1];
        memset(dp, 0, sizeof(dp));
        for(int i = 0;i < m;i++)
            for(int j = 0;j < n;j++)
                dp[j + 1] = max(dp[j],dp[j + 1]) + frame[i][j];

        return dp[n];
    }
};

代码2:

class Solution {
public:
    int jewelleryValue(vector<vector<int>>& frame) {
        int n = frame[0].size();
        int dp[n + 1];
        memset(dp, 0, sizeof(dp));
        for(auto &row : frame)
            for(int j = 0;j < n;j++)
                dp[j + 1] = max(dp[j],dp[j + 1]) + row[j];

        return dp[n];
    }
};

 

标签:return,int,frame,C++,dfs,166,Leetcode,dp,size
From: https://blog.csdn.net/2301_77329667/article/details/143312258

相关文章

  • c++ string 识别标志位并解析标志位后面的字符
    解析字符串中的固定标志位正则表达式和iterator的配合应用#include<string>#include<map>#include<regex>#include<iostream>//替换\\M+后面的字符//\\M+195B6替换为文std::regexpattern(R"(\\M+[^\\M]*)");//匹配\\M+后跟任意非\\M的字符(0次或多次)......
  • C++多线程应用
    一个进程就是一个程序,一个程序里不止一个功能,每个功能的实现就可以交给一个线程去完成。一个进程就像是一个工程,这个工程里,有设计,有监理,有施工,就相当于三个线程,各干各的又相互配合。https://cplusplus.com/reference/thread/thread/thread/是C++的官方参考,个人觉得比较权威,比经......
  • C++ 模板专题 - 标签分派(Tag Dispatching)
    一:概述:        在C++中,TagDispatching是一种编程技巧,主要用于在编译期根据不同的类型或特征选择不同的函数重载或代码分支。TagDispatching借助类型标签(tags)进行函数调度,用于在模板中实现编译期的静态分派。这种方法特别适合在泛型编程中根据类型特性(如迭代器......
  • C++:二叉搜索树(迭代)
    文章目录前言一、二叉搜索树1.二叉搜索树的概念2.二叉搜索树的操作1)遍历2)查找3)插入4)删除二、二叉搜索树的实现(迭代版本)1.二叉搜索树的结构定义2.二叉搜索树的插入3.二叉搜索树遍历4.二叉搜索树删除5.二叉搜索树查找6.二叉搜索树代码总结总结前言今天来学......
  • LeetCode30.串联所有单词的子串
    题目链接:30.串联所有单词的子串-力扣(LeetCode)1.暴力解法(会超时)由于题目中要判断s中是否有子串符合words,于是可以定义一个hashMap来存储words中的字符串的信息;定义变量len表示words中字符串的数目,strLen表示每个字符串的长度(words中的字符串长度相同);遍历s,每次取出长为len......
  • Leetcode每日一题C之3211. 生成不含相邻零的二进制字符串
    1、执行结果:通过2、显示详情:3、题目:  给你一个正整数 n。如果一个二进制字符串 x 的所有长度为2的子字符串中包含 至少 一个 "1",则称 x 是一个 有效 字符串。返回所有长度为 n 的 有效 字符串,可以以任意顺序排列。示例1:输入: n=3输出: ["010","01......
  • Leetcode每日一题C之3216. 交换后字典序最小的字符串
     1、执行结果:通过2、显示详情:3、题目:  给你一个仅由数字组成的字符串 s,在最多交换一次 相邻 且具有相同 奇偶性 的数字后,返回可以得到的字典序最小的字符串。如果两个数字都是奇数或都是偶数,则它们具有相同的奇偶性。例如,5和9、2和4奇偶性相同,而6和9奇偶......
  • 深入理解 C/C++ 中的 do-while 语句及其应用
    在C/C++编程中,do-while语句是一个重要的控制结构。它的独特之处在于,无论条件是否满足,循环体至少会执行一次。尽管do-while的基本用途是循环,但它在其他编程场合中同样具有非常巧妙和实用的应用。本文将探讨do-while语句的基本用法及其在宏定义和函数中的应用,提供高效......
  • 【信奥赛·算法基础】插入排序:算法解析与C++实现
    序言插入排序(InsertionSort)是一种简单的排序算法,就像是我们在打扑克牌时,整理手中牌的过程。一、基本原理插入排序的基本思想是:将待排序的元素插入到已经有序的部分序列中合适的位置,直到所有元素都插入完毕,整个序列就变为有序序列。二、算法步骤从第二个元素开始(假设第......
  • 【C++】踏上C++学习之旅(四):细说“内联函数“的那些事
    文章目录前言1."内联函数"被创造出来的意义2.内联函数的概念2.1内联函数在代码中的体现2.2普通函数和内联函数的汇编代码3.内联函数的特性(重点)4.总结前言本章来聊一聊C++的创作者"本贾尼"大佬,为什么要创作出内联函数,以及内联函数的定义和内联函数的实现机制等......