动态规划法

动态规划法

最长公共子序列

题目:

求两个字符串 X 和 Y 的最长公共子序列长度。例如:”abcbdab” 和 “bdcaba” 的最长公共子序列之一为 “bcba”,故应输出 4。

思路:

利用动态规划法,在二维数组 c[m+1][n+1] 中保存 $X_i$ 与 $Y_j$ 的最长公共子序列(LCS)的长度,其中 m 和 n 分别代表 X 和 Y 的长度。求解 c 的规则如下:

  • 如果 X[i] == Y[j],那么 c[i][j] = c[i-1][j-1] + 1。试想:abcd 和 abcEd,因为 d == d,我们又知道 abc 和 abcE 的 LCS 长度为 3,所以这两个字符串的 LCS 长度应为 3+1=4。
  • 如果不等,则 c[i][j] = MAX(c[i-1][j], c[i][j-1])。当前的字符不相等,故为上一次计算结果中较大的那个。
  • i == 0 或 j == 0 时,为 0。

Java 实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int getLongestCommonSubsequenceLength(String X, String Y) {
int m = X.length();
int n = Y.length();
X = " " + X; //在字符串前插入空格
Y = " " + Y;
int c[][] = new int[m+1][n+1];
for(int i=1; i<=m; i++) {
c[i][0] = 0;
}
for(int i=1; i<=n; i++) {
c[0][i] = 0;
}
for(int i=1; i<=m; i++) {
for(int j=1; j<=n; j++) {
if(X.charAt(i) == Y.charAt(j)) {
c[i][j] = c[i-1][j-1] + 1;
} else {
c[i][j] = c[i-1][j] > c[i][j-1] ? c[i-1][j] : c[i][j-1];
}
}
}
return c[m][n];
}

为什么要在字符串前插入一个无意义的空格,并把数组长度设为 m+1,n+1 呢?因为 0 列和 0 行被我们全部置零用于满足第三条条件了,所以需要多一行、一列的存储空间。当然不存,手动判断也是可以的。

硬币问题

题目:

现有面值为 c1, c2, c3, … , cm 元的 m 种硬币,求支付 n 元时所需硬币的最少枚数。各面值的硬币可任意使用 n 次。

思路:

我们用 C[i] 表示第 i 种硬币的面值。用 T[i][j] 表示使用第 0 至第 i 种硬币支付 j 元时的最少硬币数。

那么,给定某个需要支付的金额 j,求解 T[i][j] 时有两个选择,一时选用第 i 种硬币,二是不用第 i 种硬币,我们选这两者之间较小的方案即可。也就是 T[i][j] = min(T[i-1][j], T[i][j-C[i]] + 1)

其中,T[i-1][j] 表示不使用,而 T[i][j-C[i]] 表示使用,所以还要多加上使用的这枚硬币。

但是我们没必要给每种面值都记录一个最优枚数,只需要记录最小的就可以了。因此可化简为 T[j] = min(T[j], T[j-C[i]]+1)

Java 实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static int getMinNumOfCoin(int[] C, int amout) {
int[] T = new int[amout+1];
for(int i=0; i<T.length; i++) {
T[i] = Integer.MAX_VALUE;
}
T[0] = 0;
for(int i=0; i<C.length; i++) {
//外层循环遍历了使用第i种硬币 / 不使用第i种硬币的可能 我们只记录最小的情况
for(int j=C[i]; j<=amout; j++) {
T[j] = Integer.min(T[j], T[j-C[i]]+1);
}
}
return T[amout];
}

背包问题

题目:

现有价值分别为 $v_i$, 重量分别为 $w_i$ 的 N 块宝石。作为珠宝大盗的你,最多能背动总重量为 W 的背包。请问你这次盗窃的总收益最多为多少?

思路:

这道问题是每个物品选或不选的组合,因此被称为 0-1 背包问题。我们用 C[i][w] 表示前 i 个物品装入容量为 w 的背包时的总价值的最大值,递增背包容量 w 至最大值来求解。求出 C[i][w] 的值为以下二者中较大的一个:

  • C[i-1][w - 物品i的重量] + 物品i的价值
  • C[i-1][w]

第一种即选择 i 的情况,第二种为不选择 i 的情况。

Java 实现:

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
33
34
35
36
37
38
39
40
41
42
public class Main {

public static void main(String[] args) throws Exception {

Item[] items = new Item[5];
items[1] = new Item(4,2);
items[2] = new Item(5,2);
items[3] = new Item(2,1);
items[4] = new Item(8,3);

System.out.println(getMaxValue(items, 5));
}

static int getMaxValue(Item[] items, int weight) {
int[][] C = new int[items.length][weight+1];
for(int i=0; i<C.length; i++) {
for(int j=0; j<C[i].length; j++) {
C[i][j] = 0;
}
}
for(int i=1; i<items.length; i++) {
for(int j=1; j<=weight; j++) {
if(items[i].weight > j) {
//不可能选择
} else {
C[i][j] = Integer.max(C[i-1][j], C[i-1][j-items[i].weight] + items[i].value);
}
}
}
return C[items.length-1][weight];
}
}

class Item {
public int value;
public int weight;

public Item(int value, int weight) {
this.value = value;
this.weight = weight;
}
}
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×