多重背包问题 三种解法

🍑 算法题解专栏


🍑 多重背包Ⅰ

在这里插入图片描述
输入

4 5
1 2 3
2 4 1
3 4 3
4 5 2

输出

10

🍑 朴素版 O(n^3)

import java.util.Scanner;

public class Main
{
	static int N = 110;
	static int[] s = new int[N];
	static int[] w = new int[N];// 价值 weight
	static int[] v = new int[N];// 体积 volum

	static int[][] f = new int[N][N];

	public static void main(String[] args)
	{
		Scanner sc = new Scanner(System.in);
//		输入
		int n = sc.nextInt();
		int m = sc.nextInt();
		for (int i = 1; i <= n; i++)
		{
			v[i] = sc.nextInt();
			w[i] = sc.nextInt();
			s[i] = sc.nextInt();
		}
//		DP(三重循环 n^3)
		for (int i = 1; i <= n; i++)// 枚举物品
			for (int j = 1; j <= m; j++)// 枚举体积
			{	
			    f[i][j] = f[i-1][j];
			    for (int k = 0; k <= s[i]; k++)// 枚举选取当前物品的数量
			        if(j >= k * v[i])
					    f[i][j] = Math.max(f[i][j], f[i - 1][j - k * v[i]] + w[i] * k);
			}
		System.out.println(f[n][m]);
	}
}

🍑 朴素01背包版 O(n^3)

import java.util.Scanner;

public class Main
{
	static int N = 10010;
	static int[] v = new int[N];
	static int[] w = new int[N];
	static int[][] f = new int[N][110];

	public static void main(String[] args)
	{
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int m = sc.nextInt();
		int cnt = 0; // 记录物品个数
		while (n-- > 0)
		{
			int vv = sc.nextInt();
			int ww = sc.nextInt();
			int s = sc.nextInt();
			while (s-- > 0)// 拆成 01 背包
			{
				v[++cnt] = vv;
				w[cnt] = ww;
			}
		}
		for (int i = 1; i <= cnt; i++)// 枚举物品
			for (int j = 1; j <= m; j++)// 枚举体积
			{
				f[i][j] = f[i - 1][j];
				if (j >= v[i])
					f[i][j] = Math.max(f[i][j], f[i - 1][j - v[i]] + w[i]);
			}

		System.out.println(f[cnt][m]);

	}
}

🍑 滚动数组01背包版 O(n^3)

import java.util.Scanner;

public class Main
{
	static int N = 10010;
	static int[] v = new int[N];
	static int[] w = new int[N];
	static int[] f = new int[110];

	public static void main(String[] args)
	{
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int m = sc.nextInt();
		int cnt = 0; // 记录物品个数
		while (n-- > 0)
		{
			int vv = sc.nextInt();
			int ww = sc.nextInt();
			int s = sc.nextInt();
			while (s-- > 0)// 拆成 01 背包
			{
				v[++cnt] = vv;
				w[cnt] = ww;
			}
		}
		for (int i = 1; i <= cnt; i++)// 枚举物品
			for (int j = m; j >= v[i]; j--)// 枚举体积
			{
//				if (j >= v[i])
//					f[i][j] = Math.max(f[i][j], f[i - 1][j - v[i]] + w[i]);
					f[j] = Math.max(f[j], f[j - v[i]] + w[i]);
			}

		System.out.println(f[m]);

	}
}


🍑 多重背包Ⅱ

🍍 相较 Ⅰ 加大了数据范围
🍍 将数 s 分为可以组成 它 的组合,即 log(s)个数
👨‍🏫 前人之述备矣

🍑 二进制优化版 O(m * nlogn)

import java.util.Scanner;

public class Main
{
	static int N = 2010;
	static int M = N * N;
	static int f[] = new int[N];
	static int v[] = new int[M];
	static int w[] = new int[M];

	public static void main(String[] args)
	{
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int m = sc.nextInt();
		int cnt = 0;// 记录转化成 01背包后的物品个数
		for (int i = 1; i <= n; i++)
		{
			int vv = sc.nextInt();
			int ww = sc.nextInt();
			int s = sc.nextInt();
//			将 s 个物品 转化成 log(s) 份
			for (int j = 1; j <= s; j *= 2)
			{
				s -= j;
				v[++cnt] = vv * j;
				w[cnt] = ww * j;
			}
			if (s > 0)
			{
				v[++cnt] = vv * s;
				w[cnt] = ww * s;
			}
		}

//		DP
		for (int i = 1; i <= cnt; i++)
			for (int j = m; j >= v[i]; j--)
				f[j] = Math.max(f[j], f[j - v[i]] + w[i]);

		System.out.println(f[m]);
	}
}

🍑 多重背包Ⅲ

🍑 单调队列优化版 O(nm)

👨‍🏫 前人之述备矣

🍍 单调队列存的是体积

🍤 朴素版

import java.util.Scanner;

public class Main{
	static int N = 1010;
	static int M = 20010;
	static int n, m;
	static int[][] f = new int[N][M];
	static int[] v = new int[N];// 价值
	static int[] w = new int[N];// 题解
	static int[] s = new int[N];// 数量
	static int[] q = new int[M];

	public static void main(String[] args)
	{
		Scanner sc = new Scanner(System.in);
		n = sc.nextInt();
		m = sc.nextInt();
		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 r = 0; r < v[i]; r++)
			{
				int hh = 0;
				int tt = -1;
				for (int j = r; j <= m; j += v[i])
				{
					while (hh <= tt && j - q[hh] > s[i] * v[i])
						hh++;
					while (hh <= tt && f[i - 1][q[tt]] + (j - q[tt]) / v[i] * w[i] <= f[i - 1][j])
						tt--;
					q[++tt] = j;
					f[i][j] = f[i - 1][q[hh]] + (j - q[hh]) / v[i] * w[i];
				}
			}
		}
		System.out.println(f[n][m]);
	}
}

👨‍🏫 全靠大佬题解

🍤 空间优化版

import java.util.Arrays;
import java.util.Scanner;

public class Main
{
	static int N = 1010;
	static int M = 20010;
	static int[] v = new int[N];
	static int[] w = new int[N];
	static int[] s = new int[N];

	static int[] f = new int[M];// f[i][j] 前i个物品放入j容量的背包的最大价值
	static int[] pre = new int[M];
	static int[] q = new int[M];// 单调队列

	public static void main(String[] args)
	{
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int m = sc.nextInt();
//		输入
		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++)
		{
			pre = Arrays.copyOf(f, pre.length);
			for (int r = 0; r < v[i]; r++)//枚举余数
			{
				int hh = 0;
				int tt = -1;
				for (int j = r; j <= m; j += v[i])
				{
//					队头超出滑动窗口范围
					while (hh <= tt && j - q[hh] > s[i] * v[i])
						hh++;
					while (hh <= tt && pre[q[tt]] + (j - q[tt]) / v[i] * w[i] <= pre[j])
						tt--;
					q[++tt] = j;
					f[j] = pre[q[hh]] + (j - q[hh]) / v[i] * w[i];
				}
			}
		}
		System.out.println(f[m]);
	}
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值