礼物的最大价值题目改动版本(记录思路)

本文探讨了如何通过动态规划和深度优先搜索解决修改版的礼物最大价值问题,涉及从棋盘中获取能量并更新路径以求得两次行走的最大收益。关键步骤包括使用dp求解第一次走的最大能量,记录路径,更新矩阵,再用同样的方法求解第二次。
摘要由CSDN通过智能技术生成

剑指 Offer 47. 礼物的最大价值 题目改动版本

题意: 应该是在在一个 m*n 的棋盘的每一格都有一个值,表示能量(能量值大于 0)。你可以从棋盘的左上角开始走,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的能量的价值,请计算你第一次走最多能拿到多少价值的礼物?然后把你拿过的能量的棋盘的位置上的能量值置为0,然后计算第二次走最多能拿到多少价值的礼物?

解题思路:
这个题目难点的地方在于需要对拿到最大能量的走过的路径需要进行记录处理然后更新能量矩阵,对下面的能量矩阵:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
第一次走,我们能发现能拿到最大的能量是12,路径是 1→3→5→2→1 ,然后我们需要根据路径进行路径的更新,更新后的矩阵是:
[
[0,0,1],
[1,0,1],
[4,0,0]
]
之后在求第二次能拿到的最大能量,就是5,路径是0->1->->4->0->0

考虑使用DP或使用DFS搜索+memo进行解题:
(1)使用DP: 对于求第一次能量的最大值,使用dp[i][j]表示走到位置(i-1,j-1)的时候能拿到的最大能量,那么转移方程就是:
dp[i][j] = Math.max( dp[i-1][j],dp[i][j-1]) + nums[i-1][j-1]
所以使用dp求能拿到的最大能量的核心代码如下:

for(int i=1;i<=m;i++){
     for(int j=1;j<=n;j++){
  		dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1]) + grid[i-1][j-1];
   }
 }

但是我们需要找到这条最大的路径,可以利用dp状态矩阵的值,对于上面的例子,进行一次dp计算后,得到的dp状态矩阵为:
[
[1,4,5],
[2,9,10],
[6,11,12],
]
那么其实我们是可以根据dp状态矩阵找到路径的,从右下角出发,比较位置(i-1,j)(i,j-1)哪一个值更大,就是我们这个节点之前走过来的位置:所以能回退找到最大的路径的位置:12->11->9->4->1刚好就是我们的走过的最大路径,然后能够根据路径更新能量矩阵,代码如下:

int x = m;
        int y = n;
        grid[m-1][n-1] =0;
        while(x>=1&&y>=1){
        //    System.out.println(x+" "+y);
            if(dp[x-1][y]>dp[x][y-1]){
               if(x-2>=0)  grid[x-2][y-1] = 0;
                x = x - 1;
            }
            else{
               if(y-2>=0) grid[x-1][y-2] = 0;
                y = y - 1;
            }
        }

之后在求一遍dp就能拿到第二次走能获得最大能量的值了,整体代码如下:

  public int maxValue_double(int[][] grid) {
        int m  = grid.length;
        int n = grid[0].length;
        int[][] dp = new int[m+1][n+1];
        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){
                dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1]) + grid[i-1][j-1];
            }
        }
        // //打印dp状态矩阵
        // for(int i=1;i<=m;i++){
        //     for(int j=1;j<=n;j++){
        //         System.out.print(dp[i][j]+" ");
        //     }
        //     System.out.println();
        // }
        System.out.println("first " + dp[m][n]);
        int x = m;
        int y = n;
        grid[m-1][n-1] =0;
        while(x>=1&&y>=1){
        //    System.out.println(x+" "+y);
            if(dp[x-1][y]>dp[x][y-1]){
               if(x-2>=0)  grid[x-2][y-1] = 0;
                x = x - 1;
            }
            else{
               if(y-2>=0) grid[x-1][y-2] = 0;
                y = y - 1;
            }
        }
        // //打印新的
        // for(int i=0;i<m;i++){
        //     System.out.print("[");
        //     for(int j=0;j<n;j++){
        //         System.out.print(grid[i][j]);
        //         if(j!=n-1) System.out.print(",");
        //     }
        //    System.out.print("],");
        // }

        //第二次
        int[][] dp1 = new int[m+1][n+1];
        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){
                dp1[i][j] = Math.max(dp1[i-1][j],dp1[i][j-1]) + grid[i-1][j-1];
            }
        }
        System.out.println("\nsecond "+dp1[m][n]);
        return dp[m][n];
    }

(2)使用DFS+memo记录状态
在原来的只求礼物的最大价值的题目中,dfs函数返回的值是最大的礼物的价值,但是我们现在还需要记录获得能量最大的价值路径,所以把dfs的代码的返回值改成当前的能拿到的最大能量的路径就好,然后根据路径求和,然后我们选择向右走或向下走返回的路径里的和最大的哪一条进行更新就好,下面是dfs搜索的核心代码:

 public List<int[]> dfs(int x,int y,int[][] grid){
        if(x<0||x>=grid.length||y<0||y>=grid[0].length) return new LinkedList<int[]>();
        String key = x + "*" + y;
        if(memo.containsKey(key)) return memo.get(key);
        List<int[]> p1 = dfs(x+1,y,grid);
        List<int[]> p2 = dfs(x,y+1,grid);
        int sum1 = getSum(p1,grid);
        int sum2 = getSum(p2,grid);
        List<int[]> ans;
        if(sum1>sum2) ans = new LinkedList<int[]>(p1);
        else ans = new LinkedList<int[]>(p2);
        ans.add(new int[]{x,y});
        memo.put(key,ans);
        return ans;
    }

整体代码如下:

 HashMap<String,List<int[]>> memo = new HashMap<>();
    public int getSum(List<int[]> L,int[][] grid){
        int sum = 0;
        for(int i=0;i<L.size();i++){
            int[] tp = L.get(i);
            sum += grid[tp[0]][tp[1]];
        }
        return sum;
    }
    public void printt(List<int[]> res){
         for(int i=0;i<res.size();i++){
            int[] tp = res.get(i);
            System.out.print(tp[0]+" "+tp[1]+": ");
        }
        System.out.println("end1");
    }

    public List<int[]> dfs(int x,int y,int[][] grid){
        if(x<0||x>=grid.length||y<0||y>=grid[0].length) return new LinkedList<int[]>();
        String key = x + "*" + y;
        if(memo.containsKey(key)) return memo.get(key);
        List<int[]> p1 = dfs(x+1,y,grid);
        List<int[]> p2 = dfs(x,y+1,grid);
        int sum1 = getSum(p1,grid);
        int sum2 = getSum(p2,grid);
        List<int[]> ans;
        if(sum1>sum2) ans = new LinkedList<int[]>(p1);
        else ans = new LinkedList<int[]>(p2);
        ans.add(new int[]{x,y});
        memo.put(key,ans);
        return ans;
    }

    public int maxValue(int[][] grid) {
        List<int[]> res = dfs(0,0,grid);
       // printt(res);
        int sum1 = getSum(res,grid);
        //然后更新矩阵
        for(int i=0;i<res.size();i++){
            int[] tp = res.get(i);
            grid[tp[0]][tp[1]] = 0;
        }
        // for(int i=0;i<grid.length;i++){
        //     for(int j=0;j<grid[0].length;j++){
        //         System.out.print(grid[i][j]);
        //     }
        //     System.out.println();
        // }
        //需要先清空memo
        memo = new HashMap<>();
        List<int[]> res1 = dfs(0,0,grid);
        int sum2 = getSum(res1,grid);
        System.out.println("first:"+sum1+"  \nsecond:"+sum2);
        return sum1;
    }

上面例子的结果图:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值