fucking-algorithm icon indicating copy to clipboard operation
fucking-algorithm copied to clipboard

对动态规划进行降维打击 :: labuladong的算法小抄

Open labuladong opened this issue 3 years ago • 9 comments

文章链接点这里:对动态规划进行降维打击

评论礼仪 见这里,违者直接拉黑。

labuladong avatar Feb 05 '22 08:02 labuladong

strong!

xiangyueerli avatar Feb 13 '22 09:02 xiangyueerli

牛皮

yunerss avatar Feb 25 '22 08:02 yunerss

一脸懵逼的看完了。。。for (int j = i + 1; j < n; j++) {这里跟j=i+1初始条件没有任何关系。外层循环第一行定义一个变量,内层循环最后一行对这个变量赋值,这个变量就是i+1,j-1的值。循环条件是i--,j++

lzh2580 avatar Apr 27 '22 08:04 lzh2580

打卡,感谢!👍

bert82503 avatar May 24 '22 07:05 bert82503

好累

LebronHarden1 avatar Jun 18 '22 03:06 LebronHarden1

看懂啦!谢谢东哥的空间压缩~! 小而精的那块,就是i不是原先表达第i层嘛,j从左到右遍历,比如现在在遍历j,当前的j会被记录,这个dp[i][j]其实i自下而上的上一层的遗留数据,我们在用当前数据覆盖它之前将其保留,再进行下一轮内层for循环的时候,那个prev其实就是dp[i+1][j-1],而dp[j],当前还没覆盖呢,所以是下一层填数据时的老数据,即dp[i+1][j],dp[j-1]已经在处理j处的元素之前了,所以它是本轮(i)层的数据,就是dp[i][j-1],所以就都考虑到了.就想在一维数组上一直对老值做修改形成一个滚动的效果

LebronHarden1 avatar Jun 19 '22 09:06 LebronHarden1

东哥,请问自顶向下的DP如何对备忘录进行空间压缩呢?

lhcezx avatar Jul 25 '22 15:07 lhcezx

@lhcezx 不好压缩,所以一般都是把自顶向下的递归解法改成自底向上的迭代算法,然后再压缩

labuladong avatar Jul 27 '22 01:07 labuladong

东哥你好,对于文中这一句如果计算状态 dp[i][j] 需要的都是 dp[i][j] 相邻的状态,那么就可以使用空间压缩技巧有点疑惑,能不能进行空间压缩只是看dp[i][j]是否依赖dp这个二维数组中的相邻位置吗? 因为今天在做leetcode 1049 最后一块石头的重量时,写出了二维dp,代码如下:

class Solution {
    public int lastStoneWeightII(int[] stones) {
        if (stones.length == 1) {
            return stones[0];
        } else if (stones.length == 2) {
            return Math.abs(stones[0] - stones[1]);
        }

        return dp(stones);
    }

    public int dp(int[] stones) {
        int sum = 0;
        for (int stone : stones) {
            sum += stone;
        }
        int half = sum / 2;
        int[][] dp = new int[stones.length + 1][half + 1];
        Arrays.fill(dp[stones.length], 0);
        for (int i = stones.length - 1; i >= 0; i--) {
            for (int j = half; j >= 0; j--) {
                int p1 = dp[i + 1][j];
                int p2 = 0;
                if (stones[i] <= j) {
                    p2 = stones[i] + dp[i + 1][j - stones[i]];
                }
                dp[i][j] = Math.max(p1, p2);
            }
        }

        return sum - dp[0][half] * 2;
    }
}

然后抽出主要逻辑:

                int p1 = dp[i + 1][j];
                int p2 = 0;
                if (stones[i] <= j) {
                    p2 = stones[i] + dp[i + 1][j - stones[i]];
                }
                dp[i][j] = Math.max(p1, p2);

我写到这里二维数组就停下了,因为我觉得dp[i + 1][j - stones[i]]dp[i][j]需不需要不好说,而且就算需要还不一定相邻,所以就没有想着改成一维数组,但是我看评论代码随想录的评论,是写出了一维数组的代码:

class Solution {
    public int lastStoneWeightII(int[] stones) {
        int sum = 0;
        for (int i : stones) {
            sum += i;
        }
        int target = sum >> 1;
        //初始化dp数组
        int[] dp = new int[target + 1];
        for (int i = 0; i < stones.length; i++) {
            //采用倒序
            for (int j = target; j >= stones[i]; j--) {
                //两种情况,要么放,要么不放
                dp[j] = Math.max(dp[j], dp[j - stones[i]] + stones[i]);
            }
        }
        return sum - 2 * dp[target];
    }
}

dp[i][j]需要dp[i+1][j],可能需要dp[i+1][j-stones[i]],需不需要dp[i+1][j-stones[i]]还要看stones[i] <= j是否成立,我疑惑的点在于相较于本文中dp[i][j]只依赖与dp数组中相邻的位置,而本题可能还牵扯到了除了dp数组外的其他数组,那么此时判断能否空间压缩仍然是看dp[i][j]是不是只依赖dp数组中的相邻位置,还是考虑时也要带上另一个stones数组呢?感觉这里不是很明白,希望东哥能指点一下

guqihao-7 avatar Aug 06 '22 10:08 guqihao-7

pre为啥在for循环里面初始化呢?那这样不就和i-1没关系了么?

Velliy avatar Aug 15 '22 03:08 Velliy