动态规划之背包问题

1. 一维背包问题

0-1背包问题

给定一个载重量为 C C C的背包,有 n n n个物品,每个物品的重量为 w i w_{i} wi,每个物品的价值为 v i v_{i} vi,如何往背包中装物品使得背包中的总价值最大化。

m a x    V = Σ v i x i s . t .    Σ w i x i ≤ C            x i = 0   o r   1 \begin{matrix} & max ~~V = \Sigma{ v_{i}x_{i}} \\ & s.t.~~\Sigma{ w_{i}x_{i}} \le C\\ & ~~~~~~~~~~x_{i}={0~or ~1} \\ \end{matrix} max  V=Σvixis.t.  ΣwixiC          xi=0 or 1

可以用动态规划的方法对该问题进行求解。

1.1. 递推表达式

  f ( i , j )   ~f(i,j)~  f(i,j) 表示前   i   ~i~  i 物品中选择物品放入背包使得总重量不超过   j   ~j~  j ,背包的最大总价值。因此有如下所示的递推公式:

(1) f ( i , j ) = { m a x   [ f ( i − 1 ,   j − w i ) + v i , f ( i − 1 ,   j ) ] j ≥ w k f ( i − 1 ,   j ) j &lt; w k f(i, j)= \begin{cases} max~[f(i-1, ~j-w_{i})+v_{i}, f(i-1,~j)] \quad &amp; j \ge w_{k}\\ f(i-1,~j)\quad &amp; j &lt; w_{k} \end{cases} \tag{1} f(i,j)={max [f(i1, jwi)+vi,f(i1, j)]f(i1, j)jwkj<wk(1)
解释

背包的最大总价值   f ( i , j )   ~f(i,j)~  f(i,j) 有两种情况可以达到,一是,不放第   i   ~i~  i 件物品,前   i − 1   ~i-1~  i1 件物品已经把背包填满了,放不下第 i i i件物品了,那该值就是   f ( i − 1 ,   j )   ~f(i-1,~j)~  f(i1, j) ;二是,放第   i   ~i~  i 件物品,那么前   i − 1   ~i-1~  i1 件物品所占的空间只能是   j − w i   ~j-w_{i}~  jwi ,那该值就是   f ( i − 1 ,   j − w i ) + v i   ~f(i-1, ~j-w_{i})+v_{i}~  f(i1, jwi)+vi 。因此,   f ( i , j )   ~f(i,j)~  f(i,j) 就是这两个值中的较大值。

1.2. 动态规划实现

int maxValue(const std::vector<int>& weights, const std::vector<int>& values, const int& C) {
    std::vector<std::vector<int> > dp(weights.size() + 1, std::vector<int>(C + 1, 0));
    for (int i = 1; i < dp.size(); ++i) {
        for (int j = 1; j < dp[i].size(); ++j) {
            if (j >= weights[i-1])
                dp[i][j] = max(dp[i-1][j], dp[i-1][j - weights[i-1]] + values[i-1]);
        }
    }
    return dp[weights.size()][C];
}

时间复杂度为   O ( n C )   ~O(nC)~  O(nC) ,空间复杂度也为   O ( n C )   ~O(nC)~  O(nC) 

1.3. 降低空间复杂度

能不能降低空间复杂度呢?答案是肯定的。再观察式(1),可以发现 f ( i , j ) f(i,j) f(i,j)的值仅和 f ( i − 1 , ∗ ) f(i-1,*) f(i1,)有关。因此可以压缩这个维度,仅用一个一维数组表示。

int maxValue(const std::vector<int>& weights, const std::vector<int>& values, const int& C) {
    std::vector<int> dp(C + 1, 0);
    for (int i = 1; i < weights.size(); ++i) {
        // reverse order
        for (int j = C; j >= 1; --j) {
            if (j >= weights[i-1])
                dp[j] = max(dp[j], dp[j - weights[i-1]] + values[i]);
        }
    }
    return dp[C];
}

注意在内层循环中使用倒序,因为在dp[j]的取值会影响其后面的取值,如果从前向后遍历,则前面的值已经刷新了,会影响后面的值,因此在遍历时要采用倒序。

2. 二维背包问题

LeetCode 474. Ones and Zeroes

For now, suppose you are a dominator of m 0s and n 1s respectively. On the other hand, there is an array with strings consisting of only 0s and 1s.

Now your task is to find the maximum number of strings that you can form with given m 0s and n 1s. Each 0 and 1 can be used at most once.

2.1 递归公式

  f ( i , j , k )   ~f(i, j, k)~  f(i,j,k) 表示在前   i   ~i~  i 字符串中,0不超过   j   ~j~  j 个,1不超过   k   ~k~  k 个条件下,最多的字符串数量。

(2) f ( i , j , k ) = { m a x   [ f ( i − 1 ,   j − z e r o s i ,   k − o n e s i ) + 1 , f ( i − 1 ,   j ,   k ) ] j ≥ z e r o s i ,   k ≥ o n e s i f ( i − 1 ,   j ,   k ) o t h e r s f(i, j, k)= \begin{cases} max~[f(i-1, ~j-zeros_{i},~k-ones_{i})+1, f(i-1,~j,~k)] \quad &amp; j\ge zeros_{i},~k \ge ones_{i}\\ f(i-1,~j,~k)\quad &amp; others \end{cases} \tag{2} f(i,j,k)={max [f(i1, jzerosi, konesi)+1,f(i1, j, k)]f(i1, j, k)jzerosi, konesiothers(2)

同样地,可以利用二维数组实现动态规划的算法以降低空间复杂度。

int findMaxForm(vector<string>& strs, int m, int n) {
    if (strs.empty()|| m < 0 || n < 0) return 0; 
    vector<vector<int>> dp(m+1, vector<int>(n+1, 0));
    // dynamic programming
    for (int i = 0; i < strs.size(); i++) {
        int zeros = 0, ones = 0;
        for (char c : strs[i]) {
            if (c == '0') zeros++;
            if (c == '1') ones++;
        }
        for (int j = m; j >= zeros; j--) {
            for (int k = n; k >= ones; k--) {
                    dp[j][k] = max(dp[j][k], dp[j-zeros][k-ones] + 1);
            }
        }
    }
    return dp[m][n];
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值