背包问题

来源:互联网 发布:做淘宝客需要多少成本 编辑:程序博客网 时间:2024/05/16 01:08

完全背包问题

一个旅行者随身携带一个背包. 可以放入背包的物品有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 0