动态规划学习02

动态规划学习02

背包问题

小偷有一个承重为W的背包,有n件物品,第i个物品价值vi,且中wi

目标:在背包称重允许的情况下,抢到尽可能价值高的物品

  1. 利用暴力回溯法求解
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public class Test3 {
    int n = 100;//n个物品
    int[] v = new int[n];//n个物品的价值
    int[] w = new int[n];//n个物品的重量
    int W = 50;//可以携带的总重量
    /**
    * 利用暴力回溯法求解问题
    * @param idx 当前走到第idx个物品
    * @param S 身上带的物品的价值
    * @return
    */
    int rob(int idx,int S){
    //idx>=n表示已经走完了 S>W表示携带的重量超出了所能承受的重量
    if(idx >= n || S > W){
    return 0;
    }
    //在抢当前物品的情况和不抢当前物品的情况中找出最大值
    return Math.max(rob(idx + 1 , S + v[idx]),
    rob(idx + 1 , S));
    }
    }

分析该种算法的时间复杂度为O(2^n)

  1. 利用存储方式存放以及计算过的值

暴力法的O(2^n)的时间复杂度是很高的,当n稍微大一点的时候需要的时间呈指级增长 ,因此可以利用空间换时间的方法,降低时间复杂度 。

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
public class Test3 {
int n = 100;//n个物品
int[] v = new int[n];//n个物品的价值
int[] w = new int[n];//n个物品的重量
int W = 50;//可以携带的总重量
int[][] result = new int[n][];//存放已经计算过的值,防止重复计算
/**
* @param idx 当前走到第idx个物品
* @param S 身上带的物品的价值
* @return
*/
int rob(int idx,int S){
//idx>=n表示已经走完了 S>W表示携带的重量超出了所能承受的重量
if(idx >= n || S > W){
return 0;
}
if(result[idx][S]>0){
return result[idx][S];
}

//在抢当前物品的情况和不抢当前物品的情况中找出最大值
result[idx][S] = Math.max(result[idx + 1][S + v[idx]],
result[idx + 1][S]);
return result[idx][S];
}
}

那么再来分析一下时空复杂度,空间复杂度毫无疑问就是新开辟的result数组,它的每一行表示的时第idx个物品,每一列表示到当前物品时身上的物品的重量,因此空间复杂度为O(nW) ,而时间复杂度怎么计算?在这个程序中,真正运算过一遍的值也就是result数组中的值,所以时间复杂度也是O(nW),这样就是利用空间换取时间。

然而,递归的写法还是不能计算太大的值,在java中,当递归的层数多的时候会抛栈溢出异常,最好把这个程序改写成递推的方式 。

  1. 利用递推写法防止栈溢出
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
public class Test3 {
int n = 100;//n个物品
int[] v = new int[n];//n个物品的价值
int[] w = new int[n];//n个物品的重量
int W = 50;//可以携带的总重量
int[][] result = new int[n+1][];//存放已经计算过的值,防止重复计算
/**
* @param idx 当前走到第idx个物品
* @param S 身上带的物品的价值
* @return
*/
int rob(int idx,int S){
result[0][0] = 0;
for (int i=1;i<=W;i++){
result[0][i] = Integer.MIN_VALUE;
}
for(int i=1;i<=n;i++){
result[i][0] = 0;
for(int j=1;j<=W;j++){
result[i][j] = result[i - 1][j];
if(j >= w[i]){
result[i][j] = Math.max(result[i - 1][j + w[i]],
result[i][j]);
}


}
}
return result[idx][S];
}
}

我们发现,第i行的值只与i-1行有关,就是计算第i行,只需要知道i-1行即可,那么就是说只需要2行的数组就可以满足需求

  1. 能不能再减少空间复杂度
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
public class Test3 {
int n = 100;//n个物品
int[] v = new int[n];//n个物品的价值
int[] w = new int[n];//n个物品的重量
int W = 50;//可以携带的总重量
int[][] result = new int[n+1][];//存放已经计算过的值,防止重复计算
/**
* @param idx 当前走到第idx个物品
* @param S 身上带的物品的价值
* @return
*/
int rob(int idx,int S){
result[0][0] = 0;
for (int i=1;i<=W;i++){
result[0][i] = Integer.MIN_VALUE;
}
for(int i=1;i<=n;i++){
int a = i % 2;
int b = (i - 1) % 2;
result[a][0] = 0;
for(int j=1;j<=W;j++){
result[a][j] = result[b][j];
if(j >= w[i]){
result[a][j] = Math.max(result[b][j + w[i]],
result[a][j]);
}
}
}
return result[idx][S];
}
}
Your browser is out-of-date!

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

×