概念
动态规划也是一种分治思想,但是与分治法不同,动态规划把原问题分解为若干子问题,然后自底向上,先求解最小的子问题,把结果存储在表格中,再求解大的子问题时,直接从表格中查询小的子问题的解,最终得到原问题的解。
能利用动态规划解决的问题有2个特性
1.最优子结构
问题的最优解包含其子问题的最优解
2.子问题重叠
大的子问题求解时需要用到小的子问题的解,所以每次求得子问题的解时,把它们放在表格里,以后使用时可以直接查询
使用动态规划的步骤
1.分析最优解的结构特征
2.建立最优值的递归式(最为关键)
3.自底向上计算最优值,并记录到表格里
4.得出最优解
例子1——0-1背包问题
问题描述:
背包载重为load,山洞中有n个宝贝,每个宝贝的重量为wi,价值为vi,每个宝贝要么装入要么不装入,盗贼能盗取的最大价值是多少?
确定数据结构:
weight[i]:第i个物品的重量
value[i]:第i个物品的价值
dp[i][j]:前i件物品放入总载重为j的购物车可获得的最大价值
choose[i]:是否选择了第i件物品
递归式:
dp[i][j]= dp[i-1][j] , j<weight[i]
max(dp[i-1][j], dp[i-1][j-weight[i]]+value[i]) , j>=weight[i]
我们一件物品一件物品得选择放与不放,如果背包的总载重小于第i件物品的重量,我们就只能依靠前一个子问题的值;如果大于,我们就可以选择前一个子问题的值和加上了第i件物品的价值的值中的较大值。可能疑惑的是:为什么拿背包的总载重去和当前这个物品的重量去比,难道不用考虑之前已经装入背包的物品的重量吗?其实这个值dp[i-1][j-weight[i]]是有可能是0的。j和weight[i]去比,你可以理解为是防止这个数组的访问不越界。
自底向上计算:
初值dp[0][j]和dp[i][0]从所代表的意义上可以得出,其值均为0。
得出最优解:
n个物品,背包总载重为m,最大价值就为dp[n][m];
确定选择了哪些物品:比较dp[n][m]和dp[n-1][m]的大小,如果相等,则代表没有选择第n个物品,choose[n]置为false,否则,置为true;如果选择了选择第n个物品,则跳到dp[n-1][m-weight[n]](如果没有选择,则跳到dp[n-1][m]),再进行相同的操作,直至每个物品都检查完。
代码实现:
1 int zeroOneBag(vector<int>& weight, vector<int>& value, int load, vector<bool>& choose) 2 { 3 int n = weight.size() - 1;//物品的数量n,重量数组和价值数组的第0个元素均为0 4 vector<vector<int>> dp(n + 1, vector<int>(load+1)); 5 for (int i = 0; i <= n; ++i) 6 dp[i][0] = 0; 7 for (int j = 0; j <= load; ++j) 8 dp[0][j] = 0; 9 for (int i = 1; i <= n; ++i) 10 { 11 for (int j = 1; j <= load; ++j) 12 { 13 if (j < weight[i]) 14 dp[i][j] = dp[i - 1][j]; 15 else 16 dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); 17 } 18 19 } 20 /* 21 for (int i = 0; i <= n; ++i)//展示dp数组 22 { 23 for (int j = 0; j <= load; ++j) 24 { 25 cout << dp[i][j] << ' '; 26 } 27 cout << endl; 28 } 29 */ 30 for (int i = n, j = load; i >= 1; --i) 31 { 32 if (dp[i][j] > dp[i - 1][j]) 33 { 34 choose[i] = true; 35 j -= weight[i]; 36 } 37 else 38 choose[i] = false; 39 } 40 /* 41 for (int i = 1; i <= n; ++i)//展示选择了哪些物品 42 cout << choose[i] << ' '; 43 cout << endl; 44 */ 45 return dp[n][load]; 46 }
测试例:
1 int main() 2 { 3 vector<int> weight{ 0,2,5,4,2,3 }, value{ 0,6,3,5,4,6 }; 4 vector<bool> choose(6); 5 int load=10; 6 int maxiValue=zeroOneBag(weight, value, load, choose); 7 cout << "0-1背包所能装的最大价值为:"<<maxiValue << endl; 8 9 return 0; 10 }