多重背包、混合背包

多重背包模板

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;

public class Main {

	public static int MAXN = 101;

	public static int MAXW = 40001;

	public static int[] v = new int[MAXN];

	public static int[] w = new int[MAXN];

	public static int[] c = new int[MAXN];

	public static int[] dp = new int[MAXW];

	public static int n, t;

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StreamTokenizer in = new StreamTokenizer(br);
		PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
		while (in.nextToken() != StreamTokenizer.TT_EOF) {
			n = (int) in.nval;
			in.nextToken();
			t = (int) in.nval;
			for (int i = 1; i <= n; i++) {
				in.nextToken(); v[i] = (int) in.nval;
				in.nextToken(); w[i] = (int) in.nval;
				in.nextToken(); c[i] = (int) in.nval;
			}
			out.println(compute1());
		}
		out.flush();
		out.close();
		br.close();
	}

	// 严格位置依赖的动态规划
	// 时间复杂度O(n * t * 每种商品的平均个数)
	public static int compute1() {
		// dp[0][....] = 0,表示没有货物的情况下,背包容量不管是多少,最大价值都是0
		int[][] dp = new int[n + 1][t + 1];
		for (int i = 1; i <= n; i++) {
			for (int j = 0; j <= t; j++) {
				dp[i][j] = dp[i - 1][j];
				for (int k = 1; k <= c[i] && w[i] * k <= j; k++) {
					dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - k * w[i]] + k * v[i]);
				}
			}
		}
		return dp[n][t];
	}

	// 空间压缩
	// 部分测试用例超时
	// 因为没有优化枚举
	// 时间复杂度O(n * t * 每种商品的平均个数)
	public static int compute2() {
		for (int i = 1; i <= n; i++) {
			for (int j = t; j >= 0; j--) {
				for (int k = 1; k <= c[i] && w[i] * k <= j; k++) {
					dp[j] = Math.max(dp[j], dp[j - k * w[i]] + k * v[i]);
				}
			}
		}
		return dp[t];
	}

}

二进制优化

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.Arrays;

public class Main {

	public static int MAXN = 1001;

	public static int MAXW = 40001;

	// 把每一种货物根据个数做二进制分组,去生成衍生商品
	// 衍生出来的每一种商品,价值放入v、重量放入w
	public static int[] v = new int[MAXN];

	public static int[] w = new int[MAXN];

	public static int[] dp = new int[MAXW];

	public static int n, t, m;

	// 时间复杂度O(t * (log(第1种商品的个数) + log(第2种商品的个数) + ... + log(第n种商品的个数)))
	// 对每一种商品的个数取log,所以时间复杂度虽然大于O(n * t),但也不会大多少
	// 多重背包最常用的方式
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StreamTokenizer in = new StreamTokenizer(br);
		PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
		while (in.nextToken() != StreamTokenizer.TT_EOF) {
			n = (int) in.nval;
			in.nextToken();
			t = (int) in.nval;
			m = 0;
			for (int i = 1, value, weight, cnt; i <= n; i++) {
				in.nextToken(); value = (int) in.nval;
				in.nextToken(); weight = (int) in.nval;
				in.nextToken(); cnt = (int) in.nval;
				// 整个文件最重要的逻辑 : 二进制分组
				// 一般都使用这种技巧,这段代码非常重要
				// 虽然时间复杂度不如单调队列优化的版本
				// 但是好写,而且即便是比赛,时间复杂度也达标
				// 二进制分组的时间复杂度为O(log cnt)
				for (int k = 1; k <= cnt; k <<= 1) {
					v[++m] = k * value;
					w[m] = k * weight;
					cnt -= k;
				}
				if (cnt > 0) {
					v[++m] = cnt * value;
					w[m] = cnt * weight;
				}
			}
			out.println(compute());
		}
		out.flush();
		out.close();
		br.close();
	}

	// 01背包的空间压缩代码(模版)
	public static int compute() {
		Arrays.fill(dp, 0, t + 1, 0);
		for (int i = 1; i <= m; i++) {
			for (int j = t; j >= w[i]; j--) {
				dp[j] = Math.max(dp[j], dp[j - w[i]] + v[i]);
			}
		}
		return dp[t];
	}

}

单调队列优化 

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;

public class Code04_BoundedKnapsackWithMonotonicQueue {

	public static int MAXN = 101;

	public static int MAXW = 40001;

	public static int[] v = new int[MAXN];

	public static int[] w = new int[MAXN];

	public static int[] c = new int[MAXN];

	public static int[] dp = new int[MAXW];

	public static int[] queue = new int[MAXW];

	public static int l, r;

	public static int n, t;

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StreamTokenizer in = new StreamTokenizer(br);
		PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
		while (in.nextToken() != StreamTokenizer.TT_EOF) {
			n = (int) in.nval;
			in.nextToken();
			t = (int) in.nval;
			for (int i = 1; i <= n; i++) {
				in.nextToken();
				v[i] = (int) in.nval;
				in.nextToken();
				w[i] = (int) in.nval;
				in.nextToken();
				c[i] = (int) in.nval;
			}
			out.println(compute2());
		}
		out.flush();
		out.close();
		br.close();
	}

	// 严格位置依赖的动态规划 + 单调队列优化枚举
	public static int compute1() {
		int[][] dp = new int[n + 1][t + 1];
		for (int i = 1; i <= n; i++) {
			for (int mod = 0; mod <= Math.min(t, w[i] - 1); mod++) {
				l = r = 0;
				for (int j = mod; j <= t; j += w[i]) {
					while (l < r && value1(dp, i, queue[r - 1]) <= value1(dp, i, j)) {
						r--;
					}
					queue[r++] = j;
					if (queue[l] == j - w[i] * (c[i] + 1)) {
						l++;
					}
					dp[i][j] = value1(dp, i, queue[l]) + j / w[i] * v[i];
				}
			}
		}
		return dp[n][t];
	}

	// 当前来到i号货物,需要j位置的指标,返回指标值
	public static int value1(int[][] dp, int i, int j) {
		return dp[i - 1][j] - j / w[i] * v[i];
	}

	// 空间压缩的动态规划 + 单调队列优化枚举
	// 因为求dp[i][j]时需要上一行左侧的若干格子
	// 所以做空间压缩时,每一行需要从右往左求
	// 以此来保证左侧的格子还没有更新,还是"上一行"的状况
	public static int compute2() {
		for (int i = 1; i <= n; i++) {
			for (int mod = 0; mod <= Math.min(t, w[i] - 1); mod++) {
				l = r = 0;
				// 先把c[i]个的指标进入单调队列
				for (int j = t - mod, cnt = 1; j >= 0 && cnt <= c[i]; j -= w[i], cnt++) {
					while (l < r && value2(i, queue[r - 1]) <= value2(i, j)) {
						r--;
					}
					queue[r++] = j;
				}
				for (int j = t - mod, enter = j - w[i] * c[i]; j >= 0; j -= w[i], enter -= w[i]) {
					// 窗口进入enter位置的指标
					if (enter >= 0) {
						while (l < r && value2(i, queue[r - 1]) <= value2(i, enter)) {
							r--;
						}
						queue[r++] = enter;
					}
					// 计算dp[i][j]
					dp[j] = value2(i, queue[l]) + j / w[i] * v[i];
					// 窗口弹出j位置的指标
					if (queue[l] == j) {
						l++;
					}
				}
			}
		}
		return dp[t];
	}

	// 当前来到i号货物,需要j位置的指标,返回指标值
	public static int value2(int i, int j) {
		return dp[j] - j / w[i] * v[i];
	}

}

观赏樱花

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.Arrays;

// 完全背包转化为多重背包
// 再把多重背包通过二进制分组转化为01背包
public class Code03_CherryBlossomViewing {

	public static int MAXN = 100001;

	public static int MAXW = 1001;

	public static int ENOUGH = 1001;

	public static int[] v = new int[MAXN];

	public static int[] w = new int[MAXN];

	public static int[] dp = new int[MAXW];

	public static int hour1, minute1, hour2, minute2;

	public static int t, n, m;

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StreamTokenizer in = new StreamTokenizer(br);
		in.parseNumbers();
		PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
		while (in.nextToken() != StreamTokenizer.TT_EOF) {
			hour1 = (int) in.nval;
			// 跳过冒号
			in.nextToken();
			in.nextToken();
			minute1 = (int) in.nval;
			in.nextToken();
			hour2 = (int) in.nval;
			// 跳过冒号
			in.nextToken();
			in.nextToken();
			minute2 = (int) in.nval;
			if (minute1 > minute2) {
				hour2--;
				minute2 += 60;
			}
			// 计算背包容量
			t = (hour2 - hour1) * 60 + minute2 - minute1;
			in.nextToken();
			n = (int) in.nval;
			m = 0;
			for (int i = 0, cost, val, cnt; i < n; i++) {
				in.nextToken();
				cost = (int) in.nval;
				in.nextToken();
				val = (int) in.nval;
				in.nextToken();
				cnt = (int) in.nval;
				if (cnt == 0) {
					cnt = ENOUGH;
				}
				// 二进制分组
				for (int k = 1; k <= cnt; k <<= 1) {
					v[++m] = k * val;
					w[m] = k * cost;
					cnt -= k;
				}
				if (cnt > 0) {
					v[++m] = cnt * val;
					w[m] = cnt * cost;
				}
			}
			out.println(compute());
		}
		out.flush();
		out.close();
		br.close();
	}

	// 01背包的空间压缩代码(模版)
	public static int compute() {
		Arrays.fill(dp, 0, t + 1, 0);
		for (int i = 1; i <= m; i++) {
			for (int j = t; j >= w[i]; j--) {
				dp[j] = Math.max(dp[j], dp[j - w[i]] + v[i]);
			}
		}
		return dp[t];
	}

}

 混合背包:多种背包的组合与转化

// 混合背包 + 多重背包普通窗口优化
// 能成功找零的钱数种类
// 每一种货币都给定面值val[i],和拥有的数量cnt[i]
// 想知道目前拥有的货币,在钱数为1、2、3...m时
// 能找零成功的钱数有多少
// 也就是说当钱数的范围是1~m
// 返回这个范围上有多少可以找零成功的钱数
// 比如只有3元的货币,数量是5张
// m = 10
// 那么在1~10范围上,只有钱数是3、6、9时,可以成功找零
// 所以返回3表示有3种钱数可以找零成功
// 测试链接 : http://poj.org/problem?id=1742
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下的code,提交时请把类名改成"Main",可以直接通过

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.Arrays;

public class Code05_MixedKnapsack {

	public static int MAXN = 101;

	public static int MAXM = 100001;

	public static int[] val = new int[MAXN];

	public static int[] cnt = new int[MAXN];

	public static boolean[] dp = new boolean[MAXM];

	public static int n, m;

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StreamTokenizer in = new StreamTokenizer(br);
		PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
		while (in.nextToken() != StreamTokenizer.TT_EOF) {
			n = (int) in.nval;
			in.nextToken();
			m = (int) in.nval;
			if (n != 0 || m != 0) {
				for (int i = 1; i <= n; i++) {
					in.nextToken();
					val[i] = (int) in.nval;
				}
				for (int i = 1; i <= n; i++) {
					in.nextToken();
					cnt[i] = (int) in.nval;
				}
				out.println(compute());
			}
		}
		out.flush();
		out.close();
		br.close();
	}

	// 直接提供空间压缩版
	public static int compute() {
		Arrays.fill(dp, 1, m + 1, false);
		dp[0] = true;
		for (int i = 1; i <= n; i++) {
			if (cnt[i] == 1) {
				// 01背包的空间压缩实现是从右往左更新的
				for (int j = m; j >= val[i]; j--) {
					if (dp[j - val[i]]) {
						dp[j] = true;
					}
				}
			} else if (val[i] * cnt[i] > m) {
				// 完全背包的空间压缩实现是从左往右更新的
				for (int j = val[i]; j <= m; j++) {
					if (dp[j - val[i]]) {
						dp[j] = true;
					}
				}
			} else {
				// 多重背包的空间压缩实现
				// 根据余数分组
				// 每一组都是从右往左更新的
				for (int mod = 0; mod < val[i]; mod++) {
					int trueCnt = 0;
					for (int j = m - mod, size = 0; j >= 0 && size <= cnt[i]; j -= val[i], size++) {
						trueCnt += dp[j] ? 1 : 0;
					}
					for (int j = m - mod, l = j - val[i] * (cnt[i] + 1); j >= 1; j -= val[i], l -= val[i]) {
						if (dp[j]) {
							trueCnt--;
						} else {
							if (trueCnt != 0) {
								dp[j] = true;
							}
						}
						if (l >= 0) {
							trueCnt += dp[l] ? 1 : 0;
						}
					}
				}
			}
		}
		int ans = 0;
		for (int i = 1; i <= m; i++) {
			if (dp[i]) {
				ans++;
			}
		}
		return ans;
	}

}

就是按照纸币的数量分成不同类型的背包

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值