Leetcode 741. 摘樱桃 C++

本文详细解析LeetCode 741题“摘樱桃”,通过动态规划算法解决玩家在限定条件下如何摘取最多樱桃的问题。文章提供了一个优化的四维动态规划解决方案,最终降维至二维,简化计算过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Leetcode 741. 摘樱桃

题目

一个N x N的网格(grid) 代表了一块樱桃地,每个格子由以下三种数字的一种来表示:

0 表示这个格子是空的,所以你可以穿过它。
1 表示这个格子里装着一个樱桃,你可以摘到樱桃然后穿过它。
-1 表示这个格子里有荆棘,挡着你的路。
你的任务是在遵守下列规则的情况下,尽可能的摘到最多樱桃:

从位置 (0, 0) 出发,最后到达 (N-1, N-1) ,只能向下或向右走,并且只能穿越有效的格子(即只可以穿过值为0或者1的格子);
当到达 (N-1, N-1) 后,你要继续走,直到返回到 (0, 0) ,只能向上或向左走,并且只能穿越有效的格子;
当你经过一个格子且这个格子包含一个樱桃时,你将摘到樱桃并且这个格子会变成空的(值变为0);
如果在 (0, 0) 和 (N-1, N-1) 之间不存在一条可经过的路径,则没有任何一个樱桃能被摘到。
示例 1:

输入: grid =
[[0, 1, -1],
[1, 0, -1],
[1, 1, 1]]
输出: 5
解释:
玩家从(0,0)点出发,经过了向下走,向下走,向右走,向右走,到达了点(2, 2)。
在这趟单程中,总共摘到了4颗樱桃,矩阵变成了[[0,1,-1],[0,0,-1],[0,0,0]]。
接着,这名玩家向左走,向上走,向上走,向左走,返回了起始点,又摘到了1颗樱桃。
在旅程中,总共摘到了5颗樱桃,这是可以摘到的最大值了。
说明:

  • grid 是一个 N * N 的二维数组,N的取值范围是1 <= N <= 50。
  • 每一个 grid[i][j] 都是集合 {-1, 0,1}其中的一个数。
  • 可以保证起点 grid[0][0] 和终点 grid[N-1][N-1] 的值都不会是 -1。

题解

动态规划
变相思维,题目给出玩家先从左上角到右下角,再从右下角回到左上角所能摘得樱桃最大数。我们不妨看成两个人同时从左上角到右下角所能摘得樱桃最大数。
令dp[i1][j1][i2][j2]表示A、B两人分别从(i1,j1)、(i2,j2)出发到(N-1,N-1)所能摘的最多樱桃树,其中走到相同的地方只能摘一个
故dp[i1][j1][i2][j2]可以作为出发地,dp[i1][j1][i2][j2] = max(dp[i1+1][j1][i2+1][j2],dp[i1+1][j1][i2][j2+1],dp[i1][j1+1][i2+1][j2],dp[i1][j1+1][i2][j2+1]) + 0/1/2 (根据空地、是否在同一个地方判断加0/1/2,也就是grid[i1][j1]和grid[i2][j2]的信息进行确定)
另外,我们可以进行优化。根据题意可以,A、B只能往左或往右走,显然有i1+j1=i2+j2=k,k为某个常数,故dp可以改为dp[i1][i2][k],这么降维后的转义方程为 dp[i1][i2][k] = max(dp[i1][i2][k+1],dp[i1+1][i2][k+1],dp[i1][i2+1][k+1],dp[i1+1][i2+1][k+1]) + 0/1/2,显然我们可以再降维,将为2维,即在2维空间下循环最大的K次,K最大为2N-2
为了降低if语句的繁杂情况,我们可以padding一下,此时K最大为2*n-2(n = N+2)
详细过程见代码

	int cherryPickup(vector<vector<int>>& grid) {
        int n = grid.size();
        vector<vector<int>> dp(n+1,vector<int>(n+1,INT_MIN));
        if(grid[n-1][n-1] == -1)    return 0;
        dp[n-1][n-1] = grid[n-1][n-1];
        for(int k=2*n-3; k>=0; k--){
            for(int i1=max(0,k-n+1); i1<=min(n-1,k); i1++){
                /*
                j1>=0   ->  i1<=k
                j1<n    ->  i1>k-n
                */   
                int j1 = k-i1;            //由i1+j1=k来确定j1            
                for(int i2=i1; i2<=min(n-1,k); i2++){
                    int j2 = k - i2;	//由i2+j2=k来确定j2           
                    if(grid[i1][j1] == -1 || grid[i2][j2] == -1) 
                        dp[i1][i2] = INT_MIN;
                    else
                        dp[i1][i2] = grid[i1][j1] + (i1 != i2 || j1 != j2)*grid[i2][j2] + max(
                                    max(dp[i1][i2+1], dp[i1+1][i2]), 
                                    max(dp[i1+1][i2+1], dp[i1][i2]));
                }
            }
        }
        return max(0,dp[0][0]);
    }

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/cherry-pickup
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值