在以前做ACM
的时候, 总是绕不开DP
算法解题, 刚好又在项目里遇到了, 就总结一下吧. DP
算法又叫动态规划算法, 从字面可以看出它是动态的. 那么是什么样的动态呢, 可以这样理解: 根据之前的计算结果动态决定下一步的计算结果.
从一个简单的题目开始: 使用1
,3
,5
三个数, 组成13
, 需要的数字最少的方案. 一眼就能看出来是5 + 5 + 3
. 那么从算法角度来看看这个是怎么实现的吧.
要判断13
由多少构成, 那么就可以把问题看成为: 12, 10, 8
这三个数分别最小由多少构成. 这样可以一直推到最小值1
. 那么算法的步骤即从1
开始计算最小构成值为多少.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 0 = 0 -> 0不需要参数即可构成 1 = 0 + 1 => [1] -> 1个(1)即可构成 2 = 1 + 1 => [2] -> 第一个(1)是上一步的计算值, 第二个(1)是其中一个数 3 = 2 + 1 => [3]/ 0 + 3 => [1] -> 2 + 1中, (2)是上一步的计算值, 这里出现了分化, 3 可以由3个值构成, 也可以由1个值构成, 那么取小, 由1个值构成的供之后计算 4 = 3[1] + 1 => [2]/ 1[1] + 3 => [2] 5 = 4[2] + 1 => [3]/ 2[2] + 3 => [3]/ 0[0] + 5 => [1] -> 取小值1 6 = 5[1] + 1 => [2]/ 3[1] + 3 => [2]/ 1[1] + 5 => [2] 7 = 6[2] + 1 / 4[2] + 3 / 2[2] + 5 => [3] 8 = 7[3] + 1 / 5[1] + 3 / 3[1] + 5 => [2] 9 = 8[2] + 1 / 6[2] + 3 / 4[2] + 5 => [3] 10 = 9[3] + 1 => [4]/ 7[3] + 3 => [4]/ 5[1] + 5 => [2] -> 取小值2 11 = 10[2] + 1 / 8[2] + 3 / 6[2] + 5 => [3] 12 = 11[3] + 1 / 9[3] + 3 / 7[3] + 5 => [4] 13 = 12[4] + 1 => [5]/ 10[2] + 3 => [3]/ 8[2] + 5 => [3] -> 取小值3
而且可以从构成中看见, 13 = 10 + 3 / 8 + 5, 10 = 5 + 5, 8 = 5 + 3
|
这便是动态规划算法的原理, 现在用代码来实现一遍:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| int lis(int n){ int *d = new int[n + 1]; int a[] = {1, 3, 5}; d[0] = 0; for (int i = 1; i <= n; i++){ d[i] = d[i - 1] + 1; for (int j = 0; j < 3; j++){ if (a[j] <= i && d[i - a[j]] + 1 < d[i]){ d[i] = d[i - a[j]] + 1; } } } return d[n]; }
|
接下来看一个复杂一点的问题: 有编号分别为 a,b,c,d,e 的五件物品, 它们的重量分别是 2,2,6,5,4, 它们的价值分别是 6,3,5,4,6, 现在给你个承重为 10 的背包, 如何让背包里装入的物品具有最大的价值总和.
同样的计算方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| int lis(int n){ int w[] = { 0, 4, 5, 6, 2, 2 }; int v[] = { 0, 6, 4, 5, 3, 6 }; int w*length = getArrayLen(w); int \*\_d = new int*[w_length]; for (int i = 0; i < w_length; i++){ d[i] = new int[n + 1]; } for (int i = 0; i < n + 1 || i < w_length; i++){ if (i < n + 1){ d[0][i] = 0; } if (i < w_length){ d[i][0] = 0; } } for (int i = 1; i < n + 1; i++){ for (int j = 1; j < w_length; j++){ if (i >= w[j]){ int a = d[j - 1][i]; int b = d[j - 1]i - w[j]] + v[j]; d[j][i] = a > b ? a : b; } else{ d[j][i] = d[j - 1][i]; } } } return d[w_length - 1][n]; }
printf("%4d\n", lis(10));
|