动态规划学习笔记

动态规划总结

动态规划中每一个状态一定是由上一个状态推导出来的

明确两个点,状态和选择

解题步骤

1.确定dp数组以及下标的含义
2.确定递推公式
3.dp数组如何初始化
4.确定遍历顺序
5.举例推导dp数组

有一些情况递推公式决定了dp数组要如何初始化,所以先确定递推公式。

bp里面198题的有一条评论很有模板性,这里记录下来学习。
学习笔记

如何debug

最好办法是把dp数组打印出来,观察是否与解题步骤5所示自己推导的数组一样。
如果一样,那么就是递推公式、初始化或者遍历顺序有问题。
如果不一样,就是代码实现细节有问题。

背包问题

image

重点是01背包与完全背包

01背包

01背包

滚动数组

有N件物品和一个最多能放重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。

初始化数组

image

倒叙遍历,保证物品0只被放入一次

/*
dp[0][j],存放编号0的物品的时候,各个容量的背包所能存放的最大价值。
这里将的是0号物品,就不用考虑dp[i - 1][j],
*/
// 倒叙遍历
for (int j = bagWeight; j >= weight[0]; j--) {
    dp[0][j] = dp[0][j - weight[0]] + value[0]; // 初始化i为0时候的情况
}

//感觉这里可以直接初始化
for (int j = weight[0]; j >= weight[0]; j++) {
    dp[0][j] = value[0]; // 初始化i为0时候的情况
}

遍历顺序
有两个遍历维度:物品与背包重量
先遍历物品

  for(int i = 1; i < weight.size(); i++) { // 遍历物品
        for(int j = 0; j <= bagWeight; j++) { // 遍历背包容量
            if (j < weight[i]) dp[i][j] = dp[i - 1][j];
            else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);

        }
    }

image

一维写法
这就是滚动数组的由来,需要满足的条件是上一层可以重复利用,直接拷贝到当前层。不断的维护数组
于其把dp[i - 1]这一层拷贝到dp[i]上,不如只用一个一维数组了,只用dp[j](一维数组,也可以理解是一个滚动数组)。

void test_1_wei_bag_problem() {
    vector<int> weight = {1, 3, 4};
    vector<int> value = {15, 20, 30};
    int bagWeight = 4;

    // 初始化
    vector<int> dp(bagWeight + 1, 0);
    for(int i = 0; i < weight.size(); i++) { // 遍历物品
        for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
            dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
        }
    }
    cout << dp[bagWeight] << endl;
}

int main() {
    test_1_wei_bag_problem();
}

完全背包

完全背包和01背包问题唯一不同的地方就是,每种物品有无限件。

01背包和完全背包唯一不同就是体现在遍历顺序上。

01背包的遍历顺序,01背包内嵌的循环是从大到小遍历,为了保证每个物品仅被添加一次。

for(int i = 0; i < weight.size(); i++) { // 遍历物品
    for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
        dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
    }
}

那么完全背包物品可以选择多次,那么改变背包的遍历顺序,从小到大就可以了。
完全背包一维数组应该从小到大遍历,所以内外for循环的顺序可以互换。二维数组的道理也是一样的,内外for循环也可以互换。这个只针对求和。

如果求组合数就是外层for循环遍历物品,内层for遍历背包。
如果求排列数就是外层for遍历背包,内层for循环遍历物品。

// 先遍历物品,再遍历背包
for(int i = 0; i < weight.size(); i++) { // 遍历物品
    for(int j = weight[i]; j < bagWeight ; j++) { // 遍历背包容量
        dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

    }
}
posted @ 2021-09-01 16:00  rananie  阅读(43)  评论(0编辑  收藏  举报