剑指Offer47.礼物的最大价值

  • 剑指Offer47.礼物的最大价值

  • 题目:
    一个m*n的矩阵,每个格子上都放着一个礼物,且价值均 > 0;
    从左上角走到右下角的所有路径中,返回价值的最大;

  • 思路:
    1.暴力dfs递归:时间O(2 ^ (m*n)):指数时间复杂度超时,因为(除了第0行,第0列)其它每个格子需要递归2次,空间O(max(m,n)):对于一个格子,只能向右或者下,因为是dfs,所以这两个格子的递归用的是同一块系统栈;

class Solution {
public:
    int res;
    void dfs(vector<vector<int>>& grid, int i, int j, int m, int n, int val) {
        val += grid[i][j];
        if (i == m - 1 && j == n - 1) {//成功到达右下角,就更新一下结果res
            res = max(res, val);
            return;   
        }
            
        int dx[2] = {0, 1}, dy[2] = {1, 0};
        for (int k = 0; k < 2; ++k) {
            int ni = i + dx[k], nj = j + dy[k];
            if (ni >= 0 && ni < m && nj >= 0 && nj < n) dfs(grid, ni, nj, m, n, val);
        }
    }
    int maxValue(vector<vector<int>>& grid) {
        if (!grid.size() || !grid[0].size()) return 0;
        int m = grid.size(), n = grid[0].size();
        res = 0;
        dfs(grid, 0, 0, m, n, 0);

        return res;
    }
};

2.dp:时间O(mn),空间O(mn):额外需要一个m*n的dp矩阵

class Solution {
public:
    int res;
    int maxValue(vector<vector<int>>& grid) {
        if (!grid.size() || !grid[0].size()) return 0;

        int m = grid.size(), n = grid[0].size();
        vector<vector<int>> dp(m, vector<int>(n));//dp[i][j]表示从[0][0]走到[i][j]处获得的最大价值
        dp[0][0] = grid[0][0];
        for (int i = 1; i < m; ++i) dp[i][0] = dp[i - 1][0] + grid[i][0];
        for (int i = 1; i < n; ++i) dp[0][i] = dp[0][i - 1] + grid[0][i];

        for (int i = 1; i < m; ++i) {
            for (int j = 1; j < n; ++j) {
                dp[i][j] = max(dp[i-1][j], dp[i][j - 1]) + grid[i][j];//从左或上过来
            }
        }

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

//上面写法为了避免dp递推式中的i-1或j-1的越界问题,需要提前初始化第0行和第0列;
//yxc的写法:实际上,第0行第0列也是可以用dp递推式推出来的,但为了避免越界,可以整个矩阵多一行一列,直接从1开始数,这样i-1,j-1就是0,仍不会月越界,且由于grid中的数全部都是正数,可以保证dp递推式的max(0, 第一行或第一列中的某个数)=第一行或第一列中的该数
class Solution {
public:
    int res;
    int maxValue(vector<vector<int>>& grid) {
        if (!grid.size() || !grid[0].size()) return 0;

        int m = grid.size(), n = grid[0].size();
        vector<vector<int>> dp(m + 1, vector<int>(n + 1));//dp[i][j]表示从[0][0]走到[i-1][j-1]处获得的最大价值
    
        for (int i = 1; i <= m; ++i) {//这里走到m
            for (int j = 1; j <= n; ++j) {//这里走到n
                dp[i][j] = max(dp[i-1][j], dp[i][j - 1]) + grid[i - 1][j - 1];//这里[i][j]对应grid中的i-1,j-1
            }
        }

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

3.将dp数矩阵优化为一维的:时间O(m*n),O(n):只需要一个长为n的一维数组,逐行更新

class Solution {
public:
    int res;
    int maxValue(vector<vector<int>>& grid) {
        if (!grid.size() || !grid[0].size()) return 0;

        int m = grid.size(), n = grid[0].size();
        vector<int> dp(n);//dp[j]表示从[0][0]走到当前[i][j]处的可获得的最大价值
        for (int i = 0; i < m; ++i) {//当前走到哪一行
            for (int j = 0; j < n; ++j) {//当前走到哪一列
                if (j == 0) dp[j] += grid[i][0];//第0列只能从上边过来,因此需要单独考虑
                else dp[j] = max(dp[j], dp[j - 1]) + grid[i][j];//其他地方都能从左(相当于自己的左边)和上(相当于自己)过来
            }
        }

        return dp[n - 1];
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值