背包问题

完全背包问题

一个旅行者随身携带一个背包. 可以放入背包的物品有n 种, 每种物品的重量和价值分别为 wi , vi . 如果背包的最大重量限制是 b, 每种物品可以放多个. 怎样选择放入背包的物品以使得背包的价值最大 ? 不妨设上述 wi , vi , b 都是正整数.

解题思路

目标函数:装入背包的所有物品的价值达到最大
约束条件:装入背包的所有物品的重量<=b

F[k][y] 表示只允许装入前k种物品,且背包重量不超过y时背包内所有物品的最大价值。此时有两种选择:

  1. 不装第k种物品,此时只能从前k-1件物品中选,F[k][y] = F[k-1][y]
  2. 第k种物品至少装一件,(如果先装入一件第k种物品,此时剩余重量y-wk,由于每种物品可以装入多件,所以任然要在前k种物品中选择)此时F[k][y] = F[k][y-wk] + vk;

    要让背包内物品价值达到最大,则递推方程:F[k][y] = max{F[k-1][y] , F[k][y-wk] + vk}
    边界条件:
    F[0][y] = 0;
    F[k][0] = 0;
    F[k][y] = 负无穷, if y<0
    F[1][y] = (y/w1)*v1;

标记函数mark[k][y] 用于标记当F[k][y] 价值达到最大时,背包里装入物品的最大编号(此函数需要从后向前遍历==)
如果:F[k-1][y] > F[k][y-wk] + vk,则mark[k][y] =mark[k-1][y]
如果:F[k-1][y] < F[k][y-wk] + vk,则mark[k][y] =k
边界条件:
mark[k][0] = 0
mak[0][k] = 0

时空复杂度

备忘录F[k][y] 中,k与y的组合有 n*b种,每种的计算时间为常数
时间复杂度:O(n*b)
空间复杂度:O(n*b)

实现

package com.cn;

import java.util.Scanner;

public class KnapsackProblem {

    public static void main(String []args){

        int n = 0;//the number of the kinds of goods
        int b = 0;//backpack maximum weight limit

        Scanner sc = new Scanner(System.in);

        n = sc.nextInt();
        b = sc.nextInt();

        int[] w = new int[n+1];//the weight of each goods
        int[] v = new int[n+1];//the value of each goods
        int[][]mark = new int[n+1][b+1];

        for(int i = 1;i <= n;i ++){
            if(sc.hasNextInt()){
                w[i] = sc.nextInt();//实际存储的下标是从1开始的
            }
        }

        for(int i = 1;i<=n;i++){
            if(sc.hasNextInt()){
                v[i] = sc.nextInt();//实际存储的下标是从1开始的
            }
        }

        System.out.println(MaxValue(n,b,w,v,mark));
        Sign(mark,n,b,w);
    }


    public static int MaxValue(int n,int b,int[] w,int[] v,int[][] mark){

        int[][] F = new int[n+1][b+1];//实际存储的下标是从1开始的


        for(int i=1;i<=b;i++){
            F[0][i] = 0;
            F[1][i] = i/w[1]*v[1];
            mark[0][i] = 0;
        }

        for(int i=1;i<=n;i++){
            F[i][0] = 0;
            mark[i][0] = 0;
        }

        for(int k=1;k<=n;k++){
            for(int y=1;y<=b;y++){

                if(y < w[k]){
                    F[k][y] = F[k-1][y];
                    mark[k][y] = mark[k-1][y];
                }else{
                    F[k][y] = F[k-1][y] > (F[k][y-w[k]]+v[k]) ? F[k-1][y] : (F[k][y-w[k]]+v[k]);
                    mark[k][y] = F[k-1][y] > (F[k][y-w[k]]+v[k]) ? mark[k-1][y] : k;
                }


            }
        }

        return F[n][b];
    }

    //计算最终装进的每件物品的数量
    public static void Sign(int[][] mark,int n,int b,int[] w){
        int[] num = new int[n+1];
        for(int i = 1;i<=n;i++){
            num[i] = 0;
        }
        int s = mark[n][b];
        int t = b;
        while(mark[s][t] != 0){
            t = t - w[s];
            num[s]++;
            while(mark[s][t] == s){
                num[s]++;
                t = t - w[s];
            }
            s = mark[s][t];
        }

        for(int i=1;i<=n;i++){
            System.out.println("item" + i + ":" + num[i]);
        }
    }


}

空间优化

F[k][y] = max{F[k-1][y] , F[k][y-wk] + vk}
使用F[y]来表示F[k][y]、以及F[k-1][y]
使用F[y-wk]来表示F[k][y-wk]

package com.cn;

import java.util.Scanner;

public class KnapsackProblemOptimization {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        int n = 0;//the number of the kinds of goods
        int b = 0;//backpack maximum weight limit

        Scanner sc = new Scanner(System.in);

        n = sc.nextInt();
        b = sc.nextInt();

        int[] w = new int[n+1];//the weight of each goods
        int[] v = new int[n+1];//the value of each goods
        int[][]mark = new int[n+1][b+1];

        for(int i = 1;i <= n;i ++){
            if(sc.hasNextInt()){
                w[i] = sc.nextInt();
            }
        }

        for(int i = 1;i<=n;i++){
            if(sc.hasNextInt()){
                v[i] = sc.nextInt();
            }
        }

        System.out.println(MaxValue(n,b,w,v,mark));

    }

public static int MaxValue(int n,int b,int[] w,int[] v,int[][] mark){

        int[] F = new int[b+1];

        for(int i=1;i<=b;i++){

            F[i] = i/w[1]*v[1];
            mark[0][i] = 0;
        }

        for(int i=1;i<=n;i++){
            F[i] = 0;
            mark[i][0] = 0;
        }

        for(int k=2;k<=n;k++){
            for(int y=1;y<=b;y++){

                if(y < w[k]){

                    mark[k][y] = mark[k-1][y];
                }else if(F[y-w[k]]+v[k] >= F[y]){
                    F[y] = F[y-w[k]]+v[k];
                    mark[k][y] =  k;
                }

            }
        }

        return F[b];
    }

}

0-1背包问题

0-1背包问题与完全背包问题的区别就是:每种物品只有一件(要么放进背包,要么不放)

解题思路

完全按照上面的思路,但是:
F[k][y]任然有两种选择:

  1. 不装第k种物品:F[k][y] = F[k-1][y]
  2. 装第k种物品:F[k][y] = F[k-1][y-wk] + vk

此时F[k][y] = max{F[k-1][y] , F[k-1][y-wk] + vk}

边界条件:
F[0][y] = 0;
F[k][0] = 0;
F[k][y] = 负无穷, if y<0
F[1][y] = v1 , if y>w1 ,=0, if y

实现

package com.cn;

import java.util.Scanner;

public class KnapSack01 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        int n = 0;//the number of the kinds of goods
        int b = 0;//backpack maximum weight limit

        Scanner sc = new Scanner(System.in);

        n = sc.nextInt();
        b = sc.nextInt();

        int[] w = new int[n+1];//the weight of each goods
        int[] v = new int[n+1];//the value of each goods
        int[][]mark = new int[n+1][b+1];

        for(int i = 1;i <= n;i ++){
            if(sc.hasNextInt()){
                w[i] = sc.nextInt();
            }
        }

        for(int i = 1;i<=n;i++){
            if(sc.hasNextInt()){
                v[i] = sc.nextInt();
            }
        }

        System.out.println(MaxValue(n,b,w,v,mark));
        Sign(mark,n,b,w);

    }

    public static int MaxValue(int n,int b,int[] w,int[] v,int[][] mark){

        int[][] F = new int[n+1][b+1];


        for(int i=1;i<=b;i++){
            F[0][i] = 0;
            if(i >= w[1]){
                F[1][i] = v[1];
            }else{
                F[1][i] = 0;
            }

            mark[0][i] = 0;
        }

        for(int i=1;i<=n;i++){
            F[i][0] = 0;
            mark[i][0] = 0;
        }

        for(int k=1;k<=n;k++){
            for(int y=1;y<=b;y++){

                if(y < w[k]){
                    F[k][y] = F[k-1][y];
                    mark[k][y] = mark[k-1][y];
                }else{
                    F[k][y] = F[k-1][y] > (F[k-1][y-w[k]]+v[k]) ? F[k-1][y] : (F[k-1][y-w[k]]+v[k]);
                    mark[k][y] = F[k-1][y] > (F[k][y-w[k]]+v[k]) ? mark[k-1][y] : k;
                }


            }
        }

        return F[n][b];
    }

//计算最终装进的每件物品的数量
    public static void Sign(int[][] mark,int n,int b,int[] w){

        int s = mark[n][b];
        int t = b;
        while(mark[s][t] != 0){
            System.out.println("item" + s);
            t = t - w[s];
            s = mark[s][t];
        }

    }


}

二维0-1背包问题

二维背包问题:每件物品有重量 wi 和体积 ti, i =1, 2, … , n,背包总重不超过 b,体积不超过V, 如何选择物品以得到最大价值.

递推方程

若j>=wi 且 k>=ci
m[i,j,k] = max{m[i-1,j,k] , m[i-1,j-wi,k-ci] + vi}
否则
m[i,j,k] = m[i-1,j,k]
以上内容整理自屈婉玲老师的算法设计与分析,如有不足,欢迎交流

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值