动态规划入门之完全背包

来源:互联网 发布:linux我的世界服务器 编辑:程序博客网 时间:2024/06/14 09:16

完全背包问题(UKP, unbounded knapsack problem):每种物品都有无限个可用

 

最简单思路就是把完全背包拆分成01背包,就是把01背包中状态转移方程dp[i][j]=dp[i-1][j-w[i]]+v[i]相应的做点修改,也就是说01背包只考虑放与不放进去两种情况,而完全背包要考虑 放0、放1、···、放j/w[i] 的情况。

            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= m; j++) {
                    dp[i][j] = dp[i - 1][j];           //不放进去

                          for(int k=1;k<=j/w[i];j++)
                          dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - k*w[i] ] + k*v[i] );         //放进去k个

                }
            }

把第i 种物品换成M/w[i] 个0-1 背包问题中的物品,则得到了物品数为Σ M/w[i]的0-1 背包问题。时间复杂度是O(NM*Σ M/w[i])

 

但是还有一种状态转移方程:dp[i][j]=max{dp[i-1][j],dp[i][j-w[i] ]+v[i]}

这种方法也是分两种情况:第i个放进去与不放进去,但是放进去不能仅仅代表只放进去一个了,dp[i][j-1]表示j-1的空间放前i种物品,不限第i种物品有多少个,可以是0个、

1个···直到第i种物品不能再多的情况,所以dp[i][j-1]+v[i]代表至少放了一个第i种物品,当然它的前提是能放进去(j>=w[i]),所以dp[i][j]=max{dp[i-1][j],dp[i][j-w[i] ]+v[i]}已经涵盖了一个都不放与至少放一个第i种物品的情况了,也就是组成了所有可能的情况,没有遗漏。

 

接下来是例题(poj1384):

描述
给你一个储钱罐(piggy bank),往里面存硬币。存入的过程是不可逆的,要想把钱拿出来只能摔
碎储钱罐。因此,你想知道里面是否有足够多的钱,把它摔碎是值得的。
你可以通过储钱罐的重量来推测里面至少有多少钱。已知储钱罐空的时候的重量和装了硬币后
的重量,还有每种硬币的重量和面值,每种硬币的数量不限。求在最坏情况下,储钱罐里最少有多
少钱。
输入
第1 行包含一个整数T,表示有T 组测试用例。每组测试用例,第一行是两个整数E 和F,分
别表示空储钱罐的重量和装了硬币后的重量,以克(gram) 为单位,储钱罐的重量不会超过10kg,即

1 <= E <= F <= 10000。第二行是一个整数N(1 <= N <= 500),表示硬币的种类数目。接下来是N 行,
每行包含两个整数v 和w(1 <= v <= 50000; 1 <= w <= 10000),分别表示硬币的面值和重量。
输出
每个案例打印一行。内容是”e minimum amount of money in the piggy-bank is X.”,其中X 表示
储钱罐里最少有多少钱。如果不能精确地达到给定的重量,则打印”is is impossible.”。
样例输入
3
10 110
21
1
30 50
10 110
21
1
50 30
1 6
2
10 3
20 4
样例输出
The minimum amount of money in the piggy-bank is 60.
The minimum amount of money in the piggy-bank is 100.
This is impossible.

 

java代码(MLE无法AC):

import java.util.Scanner;

public class FullKnapsack {

    static final int MAX = 9999;

    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int num = scan.nextInt();
        while (num-- != 0) {
            int empty = scan.nextInt();
            int full = scan.nextInt();
            int w = full - empty;
            int n = scan.nextInt();
            int[] v = new int[n + 1];
            int[] m = new int[n + 1];
            int[][] dp = new int[n + 1][w + 1];          //dp[i][j]前i个物品凑j重量,凑得到则 < MAX,凑不到则赋MAX
            for (int i = 1; i <= n; i++) {
                v[i] = scan.nextInt();
                m[i] = scan.nextInt();

            }
            for (int j = 1; j <= w; j++) {
                dp[0][j] = MAX;                    //隐含dp[0..n][0]都为0
            }

            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= w; j++) {
                    dp[i][j] = dp[i - 1][j];          //第i个不放进去
                    if (j >= m[i]) {
                        dp[i][j] = Math.min(dp[i][j], dp[i][j - m[i]] + v[i]);        //放进去,并且1,2,3,4...取最小
                    }
              //      System.out.print(String.format("%5d",dp[i][j]));
                }
                System.out.println();
            }
            if (dp[n][w] == MAX) {
                System.out.println("This is impossible.");
            } else {
                System.out.println("The minimum amount of money in the piggy-bank is "+dp[n][w]+".");
            }

        }
    }
}

 

 

在之前的动态规划分析过程中,决策都是在可能的几种最大情况下再取最大。。

而这道题目恰相反,是取最小,而且背包必须要装满。

若是取最小,将决策中max改成min即可。

如果背包要装满,dp[0][1..w]初始值就不能为0,若是取最小,就将它赋值为极大,若取最大,则将它赋值为极小,但是注意不要越界。

判定是否装满时,就判断它是否是极大或者极小即可,但是要注意在赋值dp[i][j]的过程由于+号,可能极小值已经不再精确。

 

 下面附上能够AC的带滚动数组的java代码:

 

import java.util.Scanner;

/**
 *
 * @author Administrator
 */
public class Main {

    static final int MAX = Integer.MAX_VALUE/2;

    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int num = scan.nextInt();
        while (num-- != 0) {
            int empty = scan.nextInt();
            int full = scan.nextInt();
            int w = full - empty;
            int n = scan.nextInt();
            int[] v = new int[n + 1];
            int[] m = new int[n + 1];
            int[] dp = new int[w + 1];        
            for (int i = 1; i <= n; i++) {
                v[i] = scan.nextInt();
                m[i] = scan.nextInt();

            }
            for (int j = 1; j <= w; j++) {
                dp[j] = MAX;                   
            }

            for (int i = 1; i <= n; i++) {
                for (int j = m[i]; j <= w; j++) {
                    dp[j] = Math.min(dp[j], dp[j - m[i]] + v[i]);       
                }
            }
            if (dp[w] == MAX) {
                System.out.println("This is impossible.");
            } else {
                System.out.println("The minimum amount of money in the piggy-bank is " + dp[w] + ".");
            }

        }
    }
}

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 托福成绩失效后怎么办 去外国旅游不适应怎么办 一年级语文成绩差怎么办 一年级小孩成绩差怎么办 一年级小孩基础差怎么办 一年级小孩语文差怎么办 一年级小孩数学差怎么办 一年级小孩理解能力差怎么办 一年级小孩作文差怎么办 一年级小孩记忆力差怎么办 孩子的记忆力不好怎么办 孩子记忆力不好怎么办呢 小学三年级学生不听话怎么办 司法考试成绩单没打印怎么办 高一200分怎么办 初一孩子语文差怎么办 大学专业没选好怎么办 减肥口渴的厉害怎么办 开胸手术后胸闷气短怎么办 打游戏上网成瘾怎么办 老公打游戏成瘾怎么办 我老是沉迷游戏怎么办 18孩子沉迷游戏怎么办 志愿提交码丢失怎么办 幼儿园报名没进怎么办 考完教师资格证怎么办 两套房改房怎么办 cad打开无响应怎么办 高中生脸上痘痘怎么办 孩子玩心太大怎么办 开会听不懂讲什么怎么办 在国外玩手游老是进不去怎么办 广东普法考试不合格怎么办 尔雅考试错过了怎么办 大四考试错过了怎么办 电大考试错过了怎么办 吃了桂圆咳嗽怎么办 初中生游戏网瘾怎么办 孩子染上网瘾怎么办 郑州孩子厌学有网瘾怎么办 孩子网瘾严重怎么办