赞
踩
1.1基于动态规划的01背包问题算法实现
问题描述:
动态规划算法实现(Java):
- import java.util.Scanner;
-
- public class Packageof01 {
- public static void main(String[] args) {
- Scanner sc = new Scanner(System.in);
- //m为背包总容量,n为物品数量。
- int m = sc.nextInt();
- int n = sc.nextInt();
- /*
- carry数组:动态规划得到的最优方案
- weight数组:各个物品的重量
- value数组:每个物品的价值
- result:背包能装下的最大价值
- */
- int[][] carry = new int[n+1][m+1];
- int[] weight = new int[n+1];
- int[] value = new int[n+1];
- //输入每个物品的重量、价值。
- for (int i = 1; i <= n; i++) {
- weight[i] = sc.nextInt();
- value[i] = sc.nextInt();
- }
- //按物品重量与背包容量升序,按照当前容量能装下当前物品的最大价值,
- // 更新carry数组(物品重量与背包容量向下兼容)。
- for (int i = 1; i <= n; i++) {
- for (int j = 1; j <= m; j++) {
- if (weight[i] > j)
- carry[i][j] = carry[i - 1][j];
- else {
- carry[i][j] = Math.max(carry[i - 1][j], value[i] + carry[i - 1][j - weight[i]]);
- }
- }
- }
- //输出最优解
- /*10 4
- 2 1
- 3 3
- 4 5
- 7 9*/
- for(int i=0; i<=n;i++){
- for(int j=0; j<=m;j++){
- System.out.print(carry[i][j]+" ");
- }
- System.out.println();
- }
- System.out.println("在背包容量为"+m+"的情况下,"+"所能装下物品的最大价值为:"+carry[n][m]);
- }
- }
代码运行结果:
- 0 0 0 0 0 0 0 0 0 0 0
- 0 0 1 1 1 1 1 1 1 1 1
- 0 0 1 3 3 4 4 4 4 4 4
- 0 0 1 3 5 5 6 8 8 9 9
- 0 0 1 3 5 5 6 9 9 10 12
- 在背包容量为10的情况下,所能装下物品的最大价值为:12
时间复杂度:O(n^2)
1.2 基于贪心算法实现的部分背包问题
问题描述:对于每一个物品,可以选择放入物品全部或者物品的一部分,这种情况下选择物品的价值/物品的重量作为权重,按照权重将物品降序排序,首先放入权重最大的物品,直到不能完全放入一个物品,此时放入该物品的一部分。
贪心算法代码实现(Java):
- import java.util.Scanner;
-
- public class Package0fpart {
- //创建物品类,包括物品重量、价值、是否全部放入背包、物品的权重(p=value/weight)
-
- public static class goods {
- int weight;
- int value;
- float n;
- float p;
- }
-
- public static void Greedy(goods[] a, int n, int v) {
- for (int i = 0; i < n; i++) {
- if (v > a[i].weight) { //当物品能放入背包时,将物品全部放入背包
- a[i].n = 1;
- v -= a[i].weight;
-
- } else if (v > 0) {//当物品无法全部放入背包时,放入一部分物品
- a[i].n = (float) v / a[i].weight;
- v = 0;
- }
- }
- }
-
- //插入排序算法实现物品排序
- public static void sort(goods[] arr) {
- if (arr.length >= 2) {
- for (int i = 1; i < arr.length; i++) {
- //挖出一个要用来插入的值,同时位置上留下一个可以存新的值的坑
- goods x = arr[i];
- int j = i - 1;
- //在前面有一个或连续多个值比x大的时候,一”直循环往前面找,将x插入到这串值前面
- while (j >= 0 && arr[j].p < x.p) {
- //当arr[j]比x小的时候,将j向后移一位,正好填到坑中
- arr[j + 1] = arr[i];
- j--;
- //将x插入到最前面
- arr[i + 1] = x;
- }
- }
- }
- }
-
- public static void main(String[] args) {
- Scanner sc = new Scanner(System.in);
- System.out.println("请输入物品数量及背包容量:");
- int n = sc.nextInt();
- int v = sc.nextInt();
- goods[] a = new goods[n]; //创建goods类数组并实例化
- for (int i = 0; i < n; i++)
- a[i] = new goods();
- for (int i = 0; i < n; i++) {
- System.out.println("请输入第" + (i + 1) + "个物品的重量及价值: ");
- a[i].weight = sc.nextInt();
- a[i].value = sc.nextInt();
- a[i].p = (float) a[i].value / a[i].weight;
- }
- sort(a);
- Greedy(a, n, v);
- int sumOfvalue = 0;
- int sumOfweight = 0;
- for (int i = 0; i < n; i++) {
- if (a[i].n == 0.0)
- break;
- sumOfweight += (a[i].weight * a[i].n);
- sumOfvalue += (a[i].value * a[i].n);
- System.out.println("装入背包的第" + (i + 1) + "个物品为: ");
- System.out.println("重量: " + a[i].weight + "价值:" + a[i].value + "完整 / 部分:" + a[i].n);
- System.out.println("背包装入的总价值: " + sumOfvalue);
- System.out.println("背包装入的总重量:" + sumOfweight);
- }
- }
- }
程序运行结果:
- 请输入物品数量及背包容量:
- 3
- 50
- 请输入第1个物品的重量及价值:
- 10
- 60
- 请输入第2个物品的重量及价值:
- 20
- 100
- 请输入第3个物品的重量及价值:
- 30
- 120
- 装入背包的第1个物品为:
- 重量: 10 价值:60 完整 / 部分:1.0
- 背包装入的总价值: 60
- 背包装入的总重量:10
- 装入背包的第2个物品为:
- 重量: 20 价值:100 完整 / 部分:1.0
- 背包装入的总价值: 160
- 背包装入的总重量:30
- 装入背包的第3个物品为:
- 重量: 30 价值:120 完整 / 部分:0.6666667
- 背包装入的总价值: 240
- 背包装入的总重量:50
-
- 进程已结束,退出代码0
时间复杂度:O(n)
1.3 基于动态规划实现的完全背包问题
问题描述:对于每一个物品,其数量是无限的,对于背包容量不同的情况下,对于同一个物品,考虑放入数量为k的不同价值情况(k=0、1、2...n),选择价值最大的放置情况更新最大价值数组,直到所有的物品均被选择过。
动态规划代码实现(Java):
- import java.util.Scanner;
-
-
- public class PackageofAll {
- public static void main(String[] args) {
- Scanner sc = new Scanner(System.in);
- int n = sc.nextInt();
- int m = sc.nextInt();
- int[][] f = new int[n + 1][m + 1]; // f数组表示物品数量为i、背包容量为j的情况下的最大价值
- int[] v = new int[n]; // n个物品的体积
- int[] w = new int[n]; // n个物品的价值
- int a = 0;
- while (sc.hasNextLine() && a != n) {
- v[a] = sc.nextInt();
- w[a] = sc.nextInt();
- a++;
- }
- for (int i = 1; i <= n; i++) {
- for (int j = 0; j <= m; j++) {
- for (int k = 0; k * v[i - 1] <= j; k++) {
- f[i][j] = f[i - 1][j]; // 当背包放不下当前物品时,f取不放入该物品时的最大价值
- if (j >= v[i - 1]) // 当背包能放下当前物品时,考虑不放入该物品与放入该物品之间的最大值
- f[i][j] = Math.max(f[i - 1][j], f[i - 1][j - k * v[i - 1]] + k * w[i - 1]);
- }
- }
- }
- for (int i = 0; i <= n; i++) {
- for (int j = 0; j <= m; j++) {
- System.out.print(f[i][j] + " ");
- }
- System.out.println();
- }
- System.out.println(f[n][m]); //f数组最后一位为所有情况下的最大价值,因为背包容量越大,背包容纳的价值也越大。
- }
- }
程序运行结果:
- 3
- 4
- 1 15
- 3 20
- 4 30
- 0 0 0 0 0
- 0 15 30 45 60
- 0 15 30 45 60
- 0 15 30 45 60
- 60
-
- 进程已结束,退出代码0
时间复杂度:O(n^3)
2.1 针对01背包问题的一维数组优化
问题描述:对于01背包问题,可以考虑将二维结果数组变为一维数组,数组下标表示背包容量,数组数值表示该背包容量下的最大价值,代码改进如下:
- import java.util.Scanner;
-
- public class Packageof01 {
- public static void main(String[] args) {
- Scanner sc = new Scanner(System.in);
- //m为背包总容量,n为物品数量。
- int m = sc.nextInt();
- int n = sc.nextInt();
- /*
- carry数组:动态规划得到的最优方案
- weight数组:各个物品的重量
- value数组:每个物品的价值
- result:背包能装下的最大价值
- */
- int[] carry = new int[m + 1];
- int[] weight = new int[n + 1];
- int[] value = new int[n + 1];
- //输入每个物品的重量、价值。
- for (int i = 1; i <= n; i++) {
- weight[i] = sc.nextInt();
- value[i] = sc.nextInt();
- }
- //按物品重量与背包容量升序,按照当前容量能装下当前物品的最大价值,
- // 更新carry数组(物品重量与背包容量向下兼容)。
- for (int i = 0; i <= n; i++) {
- for (int j = m; j >= weight[i]; j--) {
- // 背包容量逆序遍历,防止重复选取物品,j>=物品重量,过滤掉放不下的情况
- carry[j] = Math.max(carry[j], value[i] + carry[j - weight[i]]);
- }
- }
-
- for (int j = 0; j <= m; j++) {
- System.out.print(carry[j] + " ");
- }
- System.out.println();
- System.out.println("在背包容量为" + m + "的情况下," + "所能装下物品的最大价值为:" + carry[m]);
- }
- }
程序运行结果:
- 10 4
- 2 1
- 3 3
- 4 5
- 7 9
- 0 0 1 3 5 5 6 9 9 10 12
- 在背包容量为10的情况下,所能装下物品的最大价值为:12
-
- 进程已结束,退出代码0
时间复杂度:O(m*n)
2.2 针对完全背包问题的一维数组优化
与01背包问题类似,可以考虑用一维数组压缩空间,与01背包不同的是,完全背包的背包容量需要顺序遍历,用到当前物品的不同数量已经考虑过的情况。程序实现如下:
- import java.util.Scanner;
-
-
- public class PackageofAll {
- public static void main(String[] args) {
- Scanner sc = new Scanner(System.in);
- int n = sc.nextInt();
- int m = sc.nextInt();
- int[] f = new int[m + 1]; // f数组表示物品数量为i、背包容量为j的情况下的最大价值
- int[] v = new int[n]; // n个物品的体积
- int[] w = new int[n]; // n个物品的价值
- int a = 0;
- while (sc.hasNextLine() && a != n) {
- v[a] = sc.nextInt();
- w[a] = sc.nextInt();
- a++;
- }
- for (int i = 1; i <= n; i++) {
- for (int j = v[i-1]; j <= m; j++) {
- // 背包容量从能放下物品开始遍历,降低时间复杂度
- f[j] = Math.max(f[j], f[j - v[i - 1]] + w[i - 1]);
- }
- }
- for (int j = 0; j <= m; j++) {
- System.out.print(f[j] + " ");
- }
- System.out.println();
- System.out.println(f[m]); //f数组最后一位为所有情况下的最大价值,因为背包容量越大,背包容纳的价值也越大。
- }
- }
程序运行结果:
- 3
- 4
- 1 15
- 3 20
- 4 30
- 0 15 30 45 60
- 60
-
- 进程已结束,退出代码0
时间复杂度:可以看到,经过一维数组优化的算法,只需两次遍历就能求得最大价值,并且背包容量从能放下物品的容量开始遍历,所以总体时间复杂度为:O(m*n)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。