[java]背包问题

背包问题

01背包

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

第 i 件物品的体积是 vi,价值是 wi

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式

第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。

接下来有 N行,每行两个整数 vi,wi,用空格隔开,分别表示第 i件物品的体积和价值。

输出格式

输出一个整数,表示最大价值。

数据范围

0<N,V≤1000
0<vi,wi≤1000

输入样例
4 5
1 2
2 4
3 4
4 5
输出样例:
8
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1010;

int n,m;
int v[N], w[N];
int f[N][N];

int main(){
    cin >> n >> m;
    for(int i = 1; i <= n; i++){
        cin >> v[i] >> w[i];
    }
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            f[i][j] = f[i - 1][j];
            if(j >= v[i]){
                f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);
            }
        }
    }
    cout << f[n][m] << endl;
}

事实上只需要一维数组保存状态即可,注意01背包j从大到小遍历,因为当**前层状态f[i]取决于上一层f[i - 1]**的状态,如果从小到大遍历j,那么当前层状态就变成从当前层变过来了

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1010;

int n,m;
int v[N], w[N];
int f[N];

int main(){
    cin >> n >> m;
    for(int i = 1; i <= n; i++){
        cin >> v[i] >> w[i];
    }
    for(int i = 1; i <= n; i++){
        for(int j = m; j >= v[i]; j--){
            f[j] = max(f[j], f[j - v[i]] + w[i]);
        }
    }
    cout << f[m] << endl;
}

思考:如果问的是背包容量恰好为V时装的最大价值怎么办

完全背包

有 N 种物品和一个容量是 V 的背包,每种物品都有无限件可用。

第 i种物品的体积是 vi,价值是 wi

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式

第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 N行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 种物品的体积和价值。

输出格式

输出一个整数,表示最大价值。

数据范围

0<N,V≤1000
0<vi,wi≤1000

输入样例
4 5
1 2
2 4
3 4
4 5
输出样例:
10
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(), m = sc.nextInt();
        int[] v = new int[n + 1], w = new int[n + 1];
        for (int i = 1; i <= n; i++) {
            v[i] = sc.nextInt();
            w[i] = sc.nextInt();
        }
        int[][] f = new int[n + 1][m + 1];
        for(int i = 1; i <= n; i++){
            for (int j = 0; j <= m; j++) {
                for (int k = 0; k * v[i] <= j; k++) {
                    f[i][j] = Math.max(f[i - 1][j - k * v[i]] + k * w[i], f[i][j]);
                }
            }
        }
        System.out.println(f[n][m]);
    }
}

优化

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(), m = sc.nextInt();
        int[] v = new int[n + 1], w = new int[n + 1];
        for (int i = 1; i <= n; i++) {
            v[i] = sc.nextInt();
            w[i] = sc.nextInt();
        }
        int[][] f = new int[n + 1][m + 1];
        for(int i = 1; i <= n; i++){
            for (int j = 0; j <= m; j++) {
                f[i][j] = f[i - 1][j];
                if(j >= v[i]){
                    f[i][j] = Math.max(f[i][j], f[i][j - v[i]] + w[i]);
                }
            }
        }
        System.out.println(f[n][m]);
    }
}

优化

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(), m = sc.nextInt();
        int[] v = new int[n + 1], w = new int[n + 1];
        for (int i = 1; i <= n; i++) {
            v[i] = sc.nextInt();
            w[i] = sc.nextInt();
        }
        int[] f = new int[m + 1];
        for(int i = 1; i <= n; i++){
            for (int j = v[i]; j <= m; j++) {
                f[j] = Math.max(f[j], f[j - v[i]] + w[i]);
            }
        }
        System.out.println(f[m]);
    }
}

多重背包

有 N 种物品和一个容量是 V的背包。

第 i种物品最多有 si件,每件体积是 vi,价值是 wi

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。

输入格式

第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。

输出格式

输出一个整数,表示最大价值。

数据范围

0<N,V≤100
0<vi,wi,si≤100

输入样例
4 5
1 2 3
2 4 1
3 4 3
4 5 2
输出样例:
10

多重背包转化为01背包

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(), m = sc.nextInt();
        int[] f = new int[m + 1], v = new int[n + 1], w = new int[n + 1], s = new int[n + 1];
        for (int i = 1; i <= n; i++) {
            v[i] = sc.nextInt();
            w[i] = sc.nextInt();
            s[i] = sc.nextInt();
        }
        for (int i = 1; i <= n; i++) {
            for (int j = m; j >= v[i]; j--) {
                for (int k = 1; k <= s[i] && k * v[i] <= j; k++) {
                    f[j] = Math.max(f[j], f[j - k * v[i]] + k * w[i]);
                }
            }
        }
        System.out.println(f[m]);
    }
}

上述多重背包的解法时间复杂度为O(n3)

优化思路:每种物品有s个,是否需要枚举1到s到底取了几个?==>不需要,我们可以用二进制表示s,那么枚举物品的个数的复杂度就从O(n)变为了O(log2n)

/**
	Good用于重新表示物品
*/
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Main {
    static class Good{
        int v;
        int w;

        public Good(int v, int w) {
            this.v = v;
            this.w = w;
        }
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(), m = sc.nextInt();
        List<Good> goods = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            int v = sc.nextInt();
            int w = sc.nextInt();
            int s = sc.nextInt();
            for (int k = 1; k <= s; k++) {
                s -= k;
                goods.add(new Good(k * v, k * w));
            }
            if(s > 0){
                goods.add(new Good(s * v, s * w));
            }
        }
        int[] f = new int[m + 1];
        for(Good good : goods){
            for(int j = m; j >= good.v; j--){
                f[j] = Math.max(f[j], f[j - good.v] + good.w);
            }
        }
        System.out.println(f[m]);
    }
}

混合背包

有 N种物品和一个容量是 V的背包。

物品一共有三类:

  • 第一类物品只能用1次(01背包);
  • 第二类物品可以用无限次(完全背包);
  • 第三类物品最多只能用 si次(多重背包);

每种体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。

输入格式

第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 N行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。

  • si=−1 表示第 i种物品只能用1次;
  • si=0 表示第 i 种物品可以用无限次;
  • si>0表示第 i种物品可以使用 si次;
输出格式

输出一个整数,表示最大价值。

数据范围

0<N,V≤≤1000
0<vi,wi≤1000
−1≤si≤1000

输入样例
4 5
1 2 -1
2 4 1
3 4 0
4 5 2
输出样例:
8

混合背包问题思路很简单,枚举每种物品的时候分情况讨论即可

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Main {
    static class Good{
        int type;
        int v;
        int w;

        public Good(int type, int v, int w) {
            this.type = type;
            this.v = v;
            this.w = w;
        }
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(), m = sc.nextInt();
        List<Good> goods = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            int v = sc.nextInt();
            int w = sc.nextInt();
            int s = sc.nextInt();
            if(s < 0){
                goods.add(new Good(-1, v, w));
            }else if(s == 0){
                goods.add(new Good(1, v, w));
            }else {
                for(int k = 1; k * v <= s; k *= 2){
                    s -= k * v;
                    goods.add(new Good(-1, k * v, k * w));
                }
                if(s > 0){
                    goods.add(new Good(-1, s * v, s * w));
                }
            }
        }
        int[] f = new int[m + 1];
        for(Good good : goods){
            if(good.type == -1){
                for(int j = m; j >= good.v; j--){
                    f[j] = Math.max(f[j], f[j - good.v] + good.w);
                }
            }else{
                for(int j = good.v; j <= m; j++){
                    f[j] = Math.max(f[j], f[j - good.v] + good.w);
                }
            }
        }
        System.out.println(f[m]);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
背包问题是一个经典的组合优化问题,它可以分为0/1背包问题和完全背包问题两种类型。其中,0/1背包问题指的是每个物品只能选择一次,而完全背包问题则是每个物品可以选择无限次。贪心算法是解决背包问题的一种常用方法,但是贪心算法并不一定能够得到最优解。 Java中实现背包问题的贪心算法,可以按照以下步骤进行: 1. 将所有物品按照单位重量的价值从大到小排序。 2. 依次将排好序的物品放入背包中,直到背包无法再放入为止。 具体实现可以参考以下Java代码: ```java public class KnapsackProblem { public static void main(String[] args) { int[] w = {2, 3, 4, 5}; // 物品重量数组 int[] v = {3, 4, 5, 6}; // 物品价值数组 int c = 8; // 背包容量 double[] r = new double[w.length]; // 单位重量的价值数组 for (int i = 0; i < w.length; i++) { r[i] = (double) v[i] / w[i]; } for (int i = 0; i < r.length - 1; i++) { // 按照单位重量的价值从大到小排序 for (int j = i + 1; j < r.length; j++) { if (r[i] < r[j]) { double temp = r[i]; r[i] = r[j]; r[j] = temp; int temp2 = w[i]; w[i] = w[j]; w[j] = temp2; int temp3 = v[i]; v[i] = v[j]; v[j] = temp3; } } } int[] x = new int[w.length]; // 物品选择数组 int cw = 0; // 当前背包重量 int cv = 0; // 当前背包价值 for (int i = 0; i < w.length; i++) { if (cw + w[i] <= c) { // 如果当前物品可以放入背包中 x[i] = 1; cw += w[i]; cv += v[i]; } else { // 如果当前物品无法放入背包中 x[i] = 0; } } System.out.println("背包中物品的总价值为:" + cv); System.out.println("物品选择数组为:" + Arrays.toString(x)); } } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值