JoeChenzzz

导航

动态规划

概念

  动态规划也是一种分治思想,但是与分治法不同,动态规划把原问题分解为若干子问题,然后自底向上,先求解最小的子问题,把结果存储在表格中,再求解大的子问题时,直接从表格中查询小的子问题的解,最终得到原问题的解。

能利用动态规划解决的问题有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] << '\t';
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] << '\t';
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 }

 

 

posted on 2018-09-20 22:50  JoeChenzzz  阅读(169)  评论(0编辑  收藏  举报