1. 程式人生 > 其它 >揹包問題初步總結--01揹包,完全揹包,有順序的完全揹包

揹包問題初步總結--01揹包,完全揹包,有順序的完全揹包

public class TestBeiBao2 {
    //常見的揹包問題有1、組合問題。2、True、False問題。3、最大最小問題。
    //1組合問題:dp[i]+=dp[i-num]
    //2true,false問題:dp[i] = dp[i] or dp[i-num]
    //3最大最小問題:dp[i] = min(dp[i], dp[i-num]+value[num])或者dp[i] = max(dp[i], dp[i-num]+value[num])

    //常見解法
    //1分析是否為揹包問題
    //2是以上三種揹包問題中的哪一種
    //3是0-1揹包問題還是完全揹包問題。也就是題目給的nums陣列中的元素是否可以重複使用
    //4如果是組合問題,是否需要考慮元素之間的順序。需要考慮順序有順序的解法,不需要考慮順序又有對應的解法

    //常見技巧
    //1如果是0-1揹包,即陣列中的元素不可重複使用,nums放在外迴圈,target在內迴圈,且內迴圈倒序;
    //2如果是完全揹包,即陣列中的元素可重複使用,nums放在外迴圈,target在內迴圈,且內迴圈正序;或者target在外迴圈,nums放在內迴圈,且內迴圈正序
    //3如果組合問題需考慮元素之間的順序,需要在上面兩者情況中選擇一種,記住:當外層迴圈遍歷nums時說明nums只可能按遍歷順序排列

    //常見問題
    //1典型01揹包
    //一維陣列(滾動陣列)01揹包
    public static void testWeightBagProblem(int[] weight, int[] value, int bagWeight) {
        int wLen = weight.length;
        //定義dp陣列:dp[j]表示揹包容量為j時,能獲得的最大價值
        int[] dp = new int[bagWeight + 1];
        //遍歷順序:先遍歷物品,再遍歷揹包容量
        for (int i = 0; i < wLen; i++) {
            for (int j = bagWeight; j >= weight[i]; j--) {
                dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
            }
        }

        for (int i = 0; i <= bagWeight; i++) {
            System.out.println(dp[i]);
        }
    }

    @Test
    public void test1() {
        int[] weight = {1, 3, 4};//重量 2 3 4 5 9
        int[] value = {15, 20, 30};//價值3 4 5 8 10
        int bagWight = 4;
        testWeightBagProblem(weight, value, bagWight);
    }

    //2典型完全揹包
    public void testCompletePackAnotherWay(int[] weight, int[] value, int bagWeight) {
        int wLen = weight.length;
        int[] dp = new int[bagWeight + 1];
        //以下兩者遍歷方式都行
        //遍歷順序:先遍歷揹包,再遍歷物品
//        for (int i = 1; i <= bagWeight; i++){ // 遍歷揹包容量
//            for (int j = 0; j < weight.length; j++){ // 遍歷物品
//                if (i - weight[j] >= 0){
//                    dp[i] = Math.max(dp[i], dp[i - weight[j]] + value[j]);
//                }
//            }
//        }
        //遍歷順序:先遍歷物品,再遍歷揹包容量
        for (int i = 0; i < wLen; i++) {
            for (int j = 1; j <= bagWeight; j++) {//j從0和1都行 因為j=0結果肯定是0(預設值)
                if (j >= weight[i]) {
                    dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
                }
            }
        }

        for (int i = 0; i <= bagWeight; i++) {
            System.out.println(dp[i]);
        }
    }

    @Test
    public void test2() {
        int[] weight = {1, 3, 4};//重量 2 3 4 5 9
        int[] value = {15, 20, 30};//價值3 4 5 8 10
        int bagWight = 4;
        testCompletePackAnotherWay(weight, value, bagWight);
    }

    //3有順序的揹包--LeetCode377
    //給你一個由 不同 整陣列成的陣列 nums ,和一個目標整數 target 。請你從 nums 中找出並返回總和為 target 的元素組合的個數。
    //情況1-順序不同的組合當做相同的組合
    public int combinationSum41(int[] nums, int target) {
        int[] dp = new int[target + 1];
        dp[0] = 1;
        for (int i = 0; i < nums.length; i++) {
            for (int j = 1; j <= target; j++) {
                if (j >= nums[i]) {
                    dp[j] += dp[j - nums[i]];
                }
            }
        }
        for (int k = 0; k <= target; k++) {
            System.out.println(dp[k]);//1 1 2 3 4
        }
        return dp[target];
    }

    @Test
    public void test3() {
        int[] nums = {1, 2, 3};
        int target = 4;
        combinationSum41(nums, target);
    }

    //情況2-順序相同的組合當做不同的組合
    public int combinationSum42(int[] nums, int target) {
        int[] dp = new int[target + 1];
        dp[0] = 1;
        for (int i = 0; i <= target; i++) {
            for (int j = 0; j < nums.length; j++) {
                if (i >= nums[j]) {
                    dp[i] += dp[i - nums[j]];//1 1 2 4 7
                }
            }
        }
        for (int k = 0; k <= target; k++) {
            System.out.println(dp[k]);
        }
        return dp[target];
    }

    @Test
    public void test4() {
        int[] nums = {1, 2, 3};
        int target = 4;
        combinationSum42(nums, target);
    }
}