zoukankan      html  css  js  c++  java
  • 背包问题

    一、01背包

    01背包在《算法竞赛入门经典》这本书里其实已经讲的很透彻了,不多说,看图。

    二、完全背包

    重点要说一下完全背包,完全背包不限制物品数量,题目可参考leetcode322零钱兑换。

    首先,可考虑通过一个二维数组进行状态转移,j列遍历钱的数目,i列遍历零钱,由于零钱不限制数量,因此可以多次添加。并且可以在当前阶段(i)添加,而不必在上一阶段(i-1)(倒序的话为i+1)的基础上进行添加。

    class Solution {
        public int coinChange(int[] coins, int amount) {
            int dp[][] = new int[coins.length+1][amount+1];    
            for(int i=0;i<coins.length+1;i++){
                Arrays.fill(dp[i], amount+1);
                dp[i][0]=0;
            }
            for(int i=1;i<=coins.length;i++){
                for(int j=1;j<=amount;j++){
                    if(j>=coins[i-1]){
                    //    dp[i][j]=Math.min( dp[i][j-coins[i-1]]+1, dp[i-1][j-coins[i-1]]+1);
                    dp[i][j]=Math.min( dp[i][j-coins[i-1]]+1, dp[i-1][j]);
                    }
                    else{
                       dp[i][j]=dp[i-1][j]; 
                    }
                    //dp[i][j]=Math.min(dp[i][j],dp[i-1][j]);
                }
            }
            return dp[coins.length][amount]>amount?-1:dp[coins.length][amount];
        }
    }
    View Code

    考虑用滚动数据优化。

    class Solution {
        public int coinChange(int[] coins, int amount) {
            int len = coins.length;
            int[] res = new int[amount+1];
            Arrays.fill(res, amount+1);
            res[0]=0;
            for(int i=0;i<len;i++){
                for(int j=1;j<=amount;j++){
                    if(j>=coins[i]){
                        res[j] = Math.min(res[j],res[j-coins[i]]+1);
                    }
                }
            }
            return res[amount]<amount+1?res[amount]:-1;
        }
    }
    View Code

    三、正序逆序问题

    很明显的是,01背包的滚动数组解法需要逆序遍历,而完全背包的解法则是用的正序遍历。

    首先说为什么01背包需要逆序遍历,这个在《算法竞赛入门经典》里同样有讲,因为使用滚动数组之后,f[j]中保存的是f(i-1,j)的值,实际上是把f(i)行与f(i-1)行压缩到了一起。那么,

    f[j-v]+W,实际上求的是i-1阶段的f[j-v]+W的值,此时f[j-v]的下标是i-1,而不是i。所以使用逆序,可以保证f[j-v]仍然是i-1阶段的值。如果正序,则会首先计算f[j-v]再计算f[j],然后计算f[j]时,f[j-v]的值已经被覆盖掉了。

    那么,为什么完全背包不担心覆盖问题呢,看一下代码。

    if(j>=coins[i-1]){
          dp[i][j]=Math.min( dp[i][j-coins[i-1]]+1, dp[i-1][j]);
    }else{
          dp[i][j]=dp[i-1][j]

    dp[i][j]的值依赖于当前行的dp[i][j-coins[i-1]],并不是依赖于上一行的dp[i-1][j-coins[i-1]],所以不用担心dp[i-1][j-coins[i-1]]的值被覆盖掉。而dp[i-1][j]的值在计算dp[i][j]之前就被获取过了,也不用担心被覆盖,所以可以正序计算。

  • 相关阅读:
    java实现九九乘法表
    for循环的阶乘
    Struts2 表单提交与execute()方法的结合使用
    Struts2 第三个程序 namespacce的用法
    java 字符串的比较compareTo
    java中的位预算
    java调用C++ DLL库方法
    Socket编程模式理解与对比
    c 高级函数的简单用法
    TCP粘包分析与处理
  • 原文地址:https://www.cnblogs.com/vactor/p/14483212.html
Copyright © 2011-2022 走看看