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

    背包问题

    在n个物品中挑选若干物品装入背包,最多能装多满?假设背包的大小为m,每个物品的大小为A[i]

    样例

    如果有4个物品[2, 3, 5, 7]

    如果背包的大小为11,可以选择[2, 3, 5]装入背包,最多可以装满10的空间。

    如果背包的大小为12,可以选择[2, 3, 7]装入背包,最多可以装满12的空间。

    函数需要返回最多能装满的空间大小。

    解题

    动态规划

    这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。

    用子问题定义状态:即f[i][j]前i个物品放入大小为j的空间里能够占用的最大体积。

    则其状态转移方程便是:

    f[i][j]=max{f[i-1][j],f[i-1][j-A[i]]+A[i]}

    不放第i个物品:f[i-1][j]

    放第i个物品:那么问题就转化为“前i-1件物品放入剩下的容量为j-A[i]的背包中”,此时能获得的最大体积就是f[i-1][j-A[i]]再加上通过放入第i件物品获得的体积A[i]

    注意上面的状态转移方程i的下标是从1开始的,下面程序是从0开始的,要适当调整

    public class Solution {
        /**
         * @param m: An integer m denotes the size of a backpack
         * @param A: Given n items with size A[i]
         * @return: The maximum size
         */
        public int backPack(int m, int[] A) {
            // write your code here
            int[][] P = new int[A.length+1][m+1];
            for(int i = 1;i<= A.length; i++){
                for(int j = m;j>=0;j--){
                    if(j>=A[i-1]){
                        P[i][j] = P[i-1][j-A[i-1]] + A[i-1];
                    }
                    P[i][j] = Math.max(P[i][j],P[i-1][j]);
                }
            }
            return P[A.length][m];
        }
    }

    或者对0的时候单独考虑

    public class Solution {
        /**
         * @param m: An integer m denotes the size of a backpack
         * @param A: Given n items with size A[i]
         * @return: The maximum size
         */
        public int backPack(int m, int[] A) {
            // write your code here
            int[][] P = new int[A.length][m+1];
            for(int i = 0;i< A.length; i++){
                for(int j = m;j>=0;j--){
                    if(i==0){ // 第0个物品可以放入空间为j的背包中,直接放入
                        if(j>=A[i])
                            P[i][j] = P[i][j-A[i]] + A[i];
                    }else{
                        if(j>=A[i]){
                            P[i][j] = P[i-1][j-A[i]] + A[i];
                        }
                        P[i][j] = Math.max(P[i][j],P[i-1][j]);    
                    }
                    
                }
            }
            return P[A.length-1][m];
        }
    }

    上面的时间空间复杂度都是O(MN)

    状态转移方程式:

    f[i][j] = Max(f[i-1][j],f[i-1][j-A[i]]+A[i])

    f[i][j]表示对前i个物品,j的空间所能够取得的最大价值

    实际上,我们需要求的是对所有的n个商品在m的空间中能够放入的最大价值

    可以修改定义一个一维矩阵,长度就是m的空间的价值

    f[j] = Max(f[j],f[j-A[i]]+A[i]) 这里就相当于对上面的矩阵进行了压缩

    public class Solution {
        /**
         * @param m: An integer m denotes the size of a backpack
         * @param A: Given n items with size A[i]
         * @return: The maximum size
         */
        public int backPack(int m, int[] A) {
            // write your code here
            int[] P = new int[m+1];
            for(int i=0;i<A.length;i++){
                for(int j=m;j>=0;j--){
                    if(j>=A[i])
                        P[j] = Math.max(P[j],P[j-A[i]] + A[i]);
                }
            }
            return P[m];
        }
    }

    参考:http://love-oriented.com/pack/P01.html

    可以输出零钱的具体方案

    package org.oj.dp;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    
    public class 换零钱问题 {
        static ArrayList<ArrayList<String>>  lists;
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            int[] A = {1,2,5,10,20,50};
    
            lists = new ArrayList<ArrayList<String>>(11);
            ArrayList<String> l1 = new ArrayList<String>();
            l1.add("");
            lists.add(l1);
            for(int i=0;i<11;i++){
                lists.add(new ArrayList<String>(10));
            }
            changeMoney(A,10);
            System.out.println(lists);
        }
        /**
         * 根据上一个零钱方案,更新当前零钱方案
         * @param A
         * @param money
         */
        private static void changeMoney(int[]A,int money){
            int[] dp = new int[money+1];
            dp[0] = 1;
            for(int i=0;i<A.length;i++){
                for(int j=A[i];j<=money;j++){
                    dp[j] +=dp[j-A[i]];
                    // 记录零钱方案
                    for(int k=0;k<lists.get(j-A[i]).size();k++){
                        lists.get(j).add(lists.get(j-A[i]).get(k)+" "+A[i]);
                    }
                }
            }
            System.out.println(Arrays.toString(dp));
        }
        /**
         * DFS实现
         * @param m
         * @param A
         * @param start
         * @param result
         * @param str
         */
        private static  void DFS(int m,int[]A,int start,ArrayList<String>result,String str){
                if(m == 0){    
                    result.add(str);
                    return;
                }
                if(A[start]> m){
                    return;
                }
                for(int i = start;i<A.length;i++){
                    DFS(m - A[i],A,i,result,str+ " "+A[i]);
                }
         }    
    
    }
  • 相关阅读:
    LeetCode(6. Z 字形变换)
    二分查找
    线性查找
    平安寿险Java面试-社招-四面(2019/08)
    希尔排序
    中移物联网Java面试-社招-三面(2019/07)
    插入排序
    选择排序
    冒泡排序
    八皇后问题
  • 原文地址:https://www.cnblogs.com/theskulls/p/5487061.html
Copyright © 2011-2022 走看看