class074 背包dp-分组背包、完全背包【算法】

news2024/12/25 23:40:45

class074 背包dp-分组背包、完全背包【算法】

算法讲解074【必备】背包dp-分组背包、完全背包

在这里插入图片描述

code1 P1757 通天之分组背包

// 分组背包(模版)
// 给定一个正数m表示背包的容量,有n个货物可供挑选
// 每个货物有自己的体积(容量消耗)、价值(获得收益)、组号(分组)
// 同一个组的物品只能挑选1件,所有挑选物品的体积总和不能超过背包容量
// 怎么挑选货物能达到价值最大,返回最大的价值
// 测试链接 : https://www.luogu.com.cn/problem/P1757
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下的所有代码,并把主类名改成"Main",可以直接通过

分组背包
dp[i][j]:1…i组容量为j的最大价值
dp[i-1][j]
dp[i][j-k]+k,k是组内选择

package class074;

// 分组背包(模版)
// 给定一个正数m表示背包的容量,有n个货物可供挑选
// 每个货物有自己的体积(容量消耗)、价值(获得收益)、组号(分组)
// 同一个组的物品只能挑选1件,所有挑选物品的体积总和不能超过背包容量
// 怎么挑选货物能达到价值最大,返回最大的价值
// 测试链接 : https://www.luogu.com.cn/problem/P1757
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下的所有代码,并把主类名改成"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 Code01_PartitionedKnapsack {

	public static int MAXN = 1001;

	public static int MAXM = 1001;

	// arr[i][0] i号物品的体积
	// arr[i][1] i号物品的价值
	// arr[i][2] i号物品的组号
	public static int[][] arr = new int[MAXN][3];

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

	public static int m, n;

	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) {
			m = (int) in.nval;
			in.nextToken();
			n = (int) in.nval;
			for (int i = 1; i <= n; i++) {
				in.nextToken();
				arr[i][0] = (int) in.nval;
				in.nextToken();
				arr[i][1] = (int) in.nval;
				in.nextToken();
				arr[i][2] = (int) in.nval;
			}
			Arrays.sort(arr, 1, n + 1, (a, b) -> a[2] - b[2]);
			out.println(compute1());
		}
		out.flush();
		out.close();
		br.close();
	}

	// 严格位置依赖的动态规划
	public static int compute1() {
		int teams = 1;
		for (int i = 2; i <= n; i++) {
			if (arr[i - 1][2] != arr[i][2]) {
				teams++;
			}
		}
		// 组的编号1~teams
		// dp[i][j] : 1~i是组的范围,每个组的物品挑一件,容量不超过j的情况下,最大收益
		int[][] dp = new int[teams + 1][m + 1];
		// dp[0][....] = 0
		for (int start = 1, end = 2, i = 1; start <= n; i++) {
			while (end <= n && arr[end][2] == arr[start][2]) {
				end++;
			}
			// start ... end-1 -> i组
			for (int j = 0; j <= m; j++) {
				// arr[start...end-1]是当前组,组号一样
				// 其中的每一件商品枚举一遍
				dp[i][j] = dp[i - 1][j];
				for (int k = start; k < end; k++) {
					// k是组内的一个商品编号
					if (j - arr[k][0] >= 0) {
						dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - arr[k][0]] + arr[k][1]);
					}
				}
			}
			// start去往下一组的第一个物品
			// 继续处理剩下的组
			start = end++;
		}
		return dp[teams][m];
	}

	// 空间压缩
	public static int compute2() {
		// dp[0][...] = 0
		Arrays.fill(dp, 0, m + 1, 0);
		for (int start = 1, end = 2; start <= n;) {
			while (end <= n && arr[end][2] == arr[start][2]) {
				end++;
			}
			// start....end-1
			for (int j = m; j >= 0; j--) {
				for (int k = start; k < end; k++) {
					if (j - arr[k][0] >= 0) {
						dp[j] = Math.max(dp[j], arr[k][1] + dp[j - arr[k][0]]);
					}
				}
			}
			start = end++;
		}
		return dp[m];
	}

}

code2 2218. 从栈中取出 K 个硬币的最大面值和

// 从栈中取出K个硬币的最大面值和
// 一张桌子上总共有 n 个硬币 栈 。每个栈有 正整数 个带面值的硬币
// 每一次操作中,你可以从任意一个栈的 顶部 取出 1 个硬币,从栈中移除它,并放入你的钱包里
// 给你一个列表 piles ,其中 piles[i] 是一个整数数组
// 分别表示第 i 个栈里 从顶到底 的硬币面值。同时给你一个正整数 k
// 请你返回在 恰好 进行 k 次操作的前提下,你钱包里硬币面值之和 最大为多少
// 测试链接 : https://leetcode.cn/problems/maximum-value-of-k-coins-from-piles/

把选择的方案看成组数,方案为选几个硬币的前缀和
dp[i][j]:1…i组容量为j选择的最大面额
dp[i-1][j]
dp[i-1][j-k]+k,k表示组内选择

package class074;

import java.util.List;

// 从栈中取出K个硬币的最大面值和
// 一张桌子上总共有 n 个硬币 栈 。每个栈有 正整数 个带面值的硬币
// 每一次操作中,你可以从任意一个栈的 顶部 取出 1 个硬币,从栈中移除它,并放入你的钱包里
// 给你一个列表 piles ,其中 piles[i] 是一个整数数组
// 分别表示第 i 个栈里 从顶到底 的硬币面值。同时给你一个正整数 k
// 请你返回在 恰好 进行 k 次操作的前提下,你钱包里硬币面值之和 最大为多少
// 测试链接 : https://leetcode.cn/problems/maximum-value-of-k-coins-from-piles/
public class Code02_MaximumValueOfKcoinsFromPiles {

	// piles是一组一组的硬币
	// m是容量,表示一定要进行m次操作
	// dp[i][j] : 1~i组上,一共拿走j个硬币的情况下,获得的最大价值
	// 1) 不要i组的硬币 : dp[i-1][j]
	// 2) i组里尝试每一种方案
	// 比如,i组里拿走前k个硬币的方案 : dp[i-1][j-k] + 从顶部开始前k个硬币的价值和
	// 枚举每一个k,选出最大值
	public static int maxValueOfCoins1(List<List<Integer>> piles, int m) {
		int n = piles.size();
		int[][] dp = new int[n + 1][m + 1];
		for (int i = 1; i <= n; i++) {
			// i从1组开始(我们的设定),但是题目中的piles是从下标0开始的
			// 所以来到i的时候,piles.get(i-1)是当前组
			List<Integer> team = piles.get(i - 1);
			int t = Math.min(team.size(), m);
			// 预处理前缀和,为了加速计算
			int[] preSum = new int[t + 1];
			for (int j = 0, sum = 0; j < t; j++) {
				sum += team.get(j);
				preSum[j + 1] = sum;
			}
			// 更新动态规划表
			for (int j = 0; j <= m; j++) {
				// 当前组一个硬币也不拿的方案
				dp[i][j] = dp[i - 1][j];
				for (int k = 1; k <= Math.min(t, j); k++) {
					dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - k] + preSum[k]);
				}
			}
		}
		return dp[n][m];
	}

	// 空间压缩
	public static int maxValueOfCoins2(List<List<Integer>> piles, int m) {
		int[] dp = new int[m + 1];
		for (List<Integer> team : piles) {
			int t = Math.min(team.size(), m);
			int[] preSum = new int[t + 1];
			for (int j = 0, sum = 0; j < t; j++) {
				sum += team.get(j);
				preSum[j + 1] = sum;
			}
			for (int j = m; j > 0; j--) {
				for (int k = 1; k <= Math.min(t, j); k++) {
					dp[j] = Math.max(dp[j], dp[j - k] + preSum[k]);
				}
			}
		}
		return dp[m];
	}

}

code3 P1616 疯狂的采药

// 完全背包(模版)
// 给定一个正数t,表示背包的容量
// 有m种货物,每种货物可以选择任意个
// 每种货物都有体积costs[i]和价值values[i]
// 返回在不超过总容量的情况下,怎么挑选货物能达到价值最大
// 返回最大的价值
// 测试链接 : https://www.luogu.com.cn/problem/P1616
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下的所有代码,并把主类名改成"Main",可以直接通过

完全背包:
dp[i][j]:1…i容量为j的最大价值
dp[i-1][j]
dp[i][j-costs[i]]+values[i],j-costs[i]>=0

package class074;

// 完全背包(模版)
// 给定一个正数t,表示背包的容量
// 有m种货物,每种货物可以选择任意个
// 每种货物都有体积costs[i]和价值values[i]
// 返回在不超过总容量的情况下,怎么挑选货物能达到价值最大
// 返回最大的价值
// 测试链接 : https://www.luogu.com.cn/problem/P1616
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下的所有代码,并把主类名改成"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 Code03_UnboundedKnapsack {

	public static int MAXM = 10001;

	public static int MAXT = 10000001;

	public static int[] cost = new int[MAXM];

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

	public static long[] dp = new long[MAXT];

	public static int t, 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) {
			t = (int) in.nval;
			in.nextToken();
			m = (int) in.nval;
			for (int i = 1; i <= m; i++) {
				in.nextToken();
				cost[i] = (int) in.nval;
				in.nextToken();
				val[i] = (int) in.nval;
			}
			out.println(compute2());
		}
		out.flush();
		out.close();
		br.close();
	}

	// 严格位置依赖的动态规划
	// 会空间不够,导致无法通过全部测试用例
	public static long compute1() {
		// dp[0][.....] = 0
		int[][] dp = new int[m + 1][t + 1];
		for (int i = 1; i <= m; i++) {
			for (int j = 0; j <= t; j++) {
				dp[i][j] = dp[i - 1][j];
				if (j - cost[i] >= 0) {
					dp[i][j] = Math.max(dp[i][j], dp[i][j - cost[i]] + val[i]);
				}
			}
		}
		return dp[m][t];
	}

	// 空间压缩
	// 可以通过全部测试用例
	public static long compute2() {
		Arrays.fill(dp, 1, t + 1, 0);
		for (int i = 1; i <= m; i++) {
			for (int j = cost[i]; j <= t; j++) {
				dp[j] = Math.max(dp[j], dp[j - cost[i]] + val[i]);
			}
		}
		return dp[t];
	}

}

code4 10. 正则表达式匹配

// 正则表达式匹配
// 给你字符串s、字符串p
// s中一定不含有’.‘、’‘字符,p中可能含有’.‘、’‘字符
// ‘.’ 表示可以变成任意字符,数量1个
// ‘’ 表示可以让 '’ 前面那个字符数量任意(甚至可以是0个)
// p中即便有’‘,一定不会出现以’‘开头的情况,也一定不会出现多个’'相邻的情况(无意义)
// 请实现一个支持 ‘.’ 和 '
’ 的正则表达式匹配
// 返回p的整个字符串能不能匹配出s的整个字符串
// 测试链接 : https://leetcode.cn/problems/regular-expression-matching/

f(s,p,i,j):s[i…]能不能被p[j…]完全匹配出来
i,j都到末尾了,匹配成功
一个没到匹配失败

p[j+1]!=‘*’
(s[i]==p[j]||p[j]=='.')&&f(s,p,i+1,j+1)

p[j+1]=='*'时
f(s,p,i,j+2),s不匹配x*
f(s,p,i+1,j),s匹配x*

package class074;

// 正则表达式匹配
// 给你字符串s、字符串p
// s中一定不含有'.'、'*'字符,p中可能含有'.'、'*'字符
// '.' 表示可以变成任意字符,数量1个
// '*' 表示可以让 '*' 前面那个字符数量任意(甚至可以是0个)
// p中即便有'*',一定不会出现以'*'开头的情况,也一定不会出现多个'*'相邻的情况(无意义)
// 请实现一个支持 '.' 和 '*' 的正则表达式匹配
// 返回p的整个字符串能不能匹配出s的整个字符串
// 测试链接 : https://leetcode.cn/problems/regular-expression-matching/
public class Code04_RegularExpressionMatching {

	public static boolean isMatch1(String str, String pat) {
		char[] s = str.toCharArray();
		char[] p = pat.toCharArray();
		return f1(s, p, 0, 0);
	}

	// s[i....]能不能被p[j....]完全匹配出来
	// p[j]这个字符,一定不是'*'
	public static boolean f1(char[] s, char[] p, int i, int j) {
		if (i == s.length) {
			// s没了
			if (j == p.length) {
				// 如果p也没了,返回true
				return true;
			} else {
				// p还剩下一些后缀
				// 如果p[j+1]是*,那么p[j..j+1]可以消掉,然后看看p[j+2....]是不是都能消掉
				return j + 1 < p.length && p[j + 1] == '*' && f1(s, p, i, j + 2);
			}
		} else if (j == p.length) {
			// s有后缀
			// p没后缀了
			return false;
		} else {
			// s有后缀
		    // p有后缀
			if (j + 1 == p.length || p[j + 1] != '*') {
				// s[i....]
				// p[j....]
				// 如果p[j+1]不是*,那么当前的字符必须能匹配:(s[i] == p[j] || p[j] == '?')
				// 同时,后续也必须匹配上:process1(s, p, i + 1, j + 1);
				return (s[i] == p[j] || p[j] == '.') && f1(s, p, i + 1, j + 1);
			} else {
				// 如果p[j+1]是*
				// 完全背包!
				// s[i....]
				// p[j....]
				// 选择1: 当前p[j..j+1]是x*,就是不让它搞定s[i],那么继续 : process1(s, p, i, j + 2)
				boolean p1 = f1(s, p, i, j + 2);
				// 选择2: 当前p[j..j+1]是x*,如果可以搞定s[i],那么继续 : process1(s, p, i + 1, j)
				// 如果可以搞定s[i] : (s[i] == p[j] || p[j] == '.')
				// 继续匹配 : process1(s, p, i + 1, j)
				boolean p2 = (s[i] == p[j] || p[j] == '.') && f1(s, p, i + 1, j);
				// 两个选择,有一个可以搞定就返回true,都无法搞定返回false
				return p1 || p2;
			}
		}
	}

	// 记忆化搜索
	public static boolean isMatch2(String str, String pat) {
		char[] s = str.toCharArray();
		char[] p = pat.toCharArray();
		int n = s.length;
		int m = p.length;
		// dp[i][j] == 0,表示没算过
		// dp[i][j] == 1,表示算过,答案是true
		// dp[i][j] == 2,表示算过,答案是false
		int[][] dp = new int[n + 1][m + 1];
		return f2(s, p, 0, 0, dp);
	}

	public static boolean f2(char[] s, char[] p, int i, int j, int[][] dp) {
		if (dp[i][j] != 0) {
			return dp[i][j] == 1;
		}
		boolean ans;
		if (i == s.length) {
			if (j == p.length) {
				ans = true;
			} else {
				ans = j + 1 < p.length && p[j + 1] == '*' && f2(s, p, i, j + 2, dp);
			}
		} else if (j == p.length) {
			ans = false;
		} else {
			if (j + 1 == p.length || p[j + 1] != '*') {
				ans = (s[i] == p[j] || p[j] == '.') && f2(s, p, i + 1, j + 1, dp);
			} else {
				ans = f2(s, p, i, j + 2, dp) || ((s[i] == p[j] || p[j] == '.') && f2(s, p, i + 1, j, dp));
			}
		}
		dp[i][j] = ans ? 1 : 2;
		return ans;
	}

	// 严格位置依赖的动态规划
	public static boolean isMatch3(String str, String pat) {
		char[] s = str.toCharArray();
		char[] p = pat.toCharArray();
		int n = s.length;
		int m = p.length;
		boolean[][] dp = new boolean[n + 1][m + 1];
		dp[n][m] = true;
		for (int j = m - 1; j >= 0; j--) {
			dp[n][j] = j + 1 < m && p[j + 1] == '*' && dp[n][j + 2];
		}
		for (int i = n - 1; i >= 0; i--) {
			for (int j = m - 1; j >= 0; j--) {
				if (j + 1 == m || p[j + 1] != '*') {
					dp[i][j] = (s[i] == p[j] || p[j] == '.') && dp[i + 1][j + 1];
				} else {
					dp[i][j] = dp[i][j + 2] || ((s[i] == p[j] || p[j] == '.') && dp[i + 1][j]);
				}
			}
		}
		return dp[0][0];
	}

}

code5 44. 通配符匹配

// 通配符匹配(和题目4高度相似,只是边界条件不同而已,而且更简单)
// 给你字符串s、字符串p
// s中一定不含有’?‘、’‘字符,p中可能含有’?‘、’'字符
// ‘?’ 表示可以变成任意字符,数量1个
// ‘’ 表示可以匹配任何字符串
// 请实现一个支持 ‘?’ 和 '
’ 的通配符匹配
// 返回p的整个字符串能不能匹配出s的整个字符串
// 测试链接 : https://leetcode.cn/problems/wildcard-matching/

package class074;

// 通配符匹配(和题目4高度相似,只是边界条件不同而已,而且更简单)
// 给你字符串s、字符串p
// s中一定不含有'?'、'*'字符,p中可能含有'?'、'*'字符
// '?' 表示可以变成任意字符,数量1个
// '*' 表示可以匹配任何字符串
// 请实现一个支持 '?' 和 '*' 的通配符匹配
// 返回p的整个字符串能不能匹配出s的整个字符串
// 测试链接 : https://leetcode.cn/problems/wildcard-matching/
public class Code05_WildcardMatching {

	// 暴力递归
	public static boolean isMatch1(String str, String pat) {
		char[] s = str.toCharArray();
		char[] p = pat.toCharArray();
		return f1(s, p, 0, 0);
	}

	// s[i....]能不能被p[j....]完全匹配出来
	public static boolean f1(char[] s, char[] p, int i, int j) {
		if (i == s.length) {
			// s没了
			if (j == p.length) {
				// 如果p也没了,返回true
				return true;
			} else {
				// 如果p[j]是*,可以消掉,然后看看p[j+1....]是不是都能消掉
				return p[j] == '*' && f1(s, p, i, j + 1);
			}
		} else if (j == p.length) {
			// s有
			// p没了
			return false;
		} else {
			if (p[j] != '*') {
				// s[i....]
				// p[j....]
				// 如果p[j]不是*,那么当前的字符必须能匹配:(s[i] == p[j] || p[j] == '?')
				// 同时,后续也必须匹配上:process1(s, p, i + 1, j + 1);
				return (s[i] == p[j] || p[j] == '?') && f1(s, p, i + 1, j + 1);
			} else {
				// s[i....]
				// p[j....]
				// 如果p[j]是*
				// 选择1: 反正当前p[j]是*,必然可以搞定s[i],那么继续 : process1(s, p, i + 1, j)
				// 选择2: 虽然当前p[j]是*,但就是不让它搞定s[i],那么继续 : process1(s, p, i, j + 1)
				// 两种选择有一个能走通,答案就是true;如果都搞不定,答案就是false
				return f1(s, p, i + 1, j) || f1(s, p, i, j + 1);
			}
		}
	}

	// 记忆化搜索
	public static boolean isMatch2(String str, String pat) {
		char[] s = str.toCharArray();
		char[] p = pat.toCharArray();
		int n = s.length;
		int m = p.length;
		// dp[i][j] == 0,表示没算过
		// dp[i][j] == 1,表示算过,答案是true
		// dp[i][j] == 2,表示算过,答案是false
		int[][] dp = new int[n + 1][m + 1];
		return f2(s, p, 0, 0, dp);
	}

	public static boolean f2(char[] s, char[] p, int i, int j, int[][] dp) {
		if (dp[i][j] != 0) {
			return dp[i][j] == 1;
		}
		boolean ans;
		if (i == s.length) {
			if (j == p.length) {
				ans = true;
			} else {
				ans = p[j] == '*' && f2(s, p, i, j + 1, dp);
			}
		} else if (j == p.length) {
			ans = false;
		} else {
			if (p[j] != '*') {
				ans = (s[i] == p[j] || p[j] == '?') && f2(s, p, i + 1, j + 1, dp);
			} else {
				ans = f2(s, p, i + 1, j, dp) || f2(s, p, i, j + 1, dp);
			}
		}
		dp[i][j] = ans ? 1 : 2;
		return ans;
	}

	// 严格位置依赖的动态规划
	public static boolean isMatch3(String str, String pat) {
		char[] s = str.toCharArray();
		char[] p = pat.toCharArray();
		int n = s.length;
		int m = p.length;
		boolean[][] dp = new boolean[n + 1][m + 1];
		dp[n][m] = true;
		for (int j = m - 1; j >= 0 && p[j] == '*'; j--) {
			dp[n][j] = true;
		}
		for (int i = n - 1; i >= 0; i--) {
			for (int j = m - 1; j >= 0; j--) {
				if (p[j] != '*') {
					dp[i][j] = (s[i] == p[j] || p[j] == '?') && dp[i + 1][j + 1];
				} else {
					dp[i][j] = dp[i + 1][j] || dp[i][j + 1];
				}
			}
		}
		return dp[0][0];
	}

}

code6 P2918 [USACO08NOV] Buying Hay S

// 购买足量干草的最小花费
// 有n个提供干草的公司,每个公司都有两个信息
// cost[i]代表购买1次产品需要花的钱
// val[i]代表购买1次产品所获得的干草数量
// 每个公司的产品都可以购买任意次
// 你一定要至少购买h数量的干草,返回最少要花多少钱
// 测试链接 : https://www.luogu.com.cn/problem/P2918
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下的所有代码,并把主类名改成"Main",可以直接通过

package class074;

// 购买足量干草的最小花费
// 有n个提供干草的公司,每个公司都有两个信息
// cost[i]代表购买1次产品需要花的钱
// val[i]代表购买1次产品所获得的干草数量
// 每个公司的产品都可以购买任意次
// 你一定要至少购买h数量的干草,返回最少要花多少钱
// 测试链接 : https://www.luogu.com.cn/problem/P2918
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下的所有代码,并把主类名改成"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 Code06_BuyingHayMinimumCost {

	public static int MAXN = 101;

	public static int MAXM = 55001;

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

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

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

	public static int n, h, maxv, 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();
			h = (int) in.nval;
			maxv = 0;
			for (int i = 1; i <= n; i++) {
				in.nextToken();
				val[i] = (int) in.nval;
				maxv = Math.max(maxv, val[i]);
				in.nextToken();
				cost[i] = (int) in.nval;
			}
			// 最核心的一句
			// 包含重要分析
			m = h + maxv;
			out.println(compute2());
		}
		out.flush();
		out.close();
		br.close();
	}

	// dp[i][j] : 1...i里挑公司,购买严格j磅干草,需要的最少花费
	// 1) dp[i-1][j]
	// 2) dp[i][j-val[i]] + cost[i]
	// 两种可能性中选最小
	// 但是关于j需要进行一定的扩充,原因视频里讲了
	public static int compute1() {
		int[][] dp = new int[n + 1][m + 1];
		Arrays.fill(dp[0], 1, m + 1, Integer.MAX_VALUE);
		for (int i = 1; i <= n; i++) {
			for (int j = 0; j <= m; j++) {
				dp[i][j] = dp[i - 1][j];
				if (j - val[i] >= 0 && dp[i][j - val[i]] != Integer.MAX_VALUE) {
					dp[i][j] = Math.min(dp[i][j], dp[i][j - val[i]] + cost[i]);
				}
			}
		}
		int ans = Integer.MAX_VALUE;
		// >= h
		// h h+1 h+2 ... m
		for (int j = h; j <= m; j++) {
			ans = Math.min(ans, dp[n][j]);
		}
		return ans;
	}

	// 空间压缩
	public static int compute2() {
		Arrays.fill(dp, 1, m + 1, Integer.MAX_VALUE);
		for (int i = 1; i <= n; i++) {
			for (int j = val[i]; j <= m; j++) {
				if (dp[j - val[i]] != Integer.MAX_VALUE) {
					dp[j] = Math.min(dp[j], dp[j - val[i]] + cost[i]);
				}
			}
		}
		int ans = Integer.MAX_VALUE;
		for (int j = h; j <= m; j++) {
			ans = Math.min(ans, dp[j]);
		}
		return ans;
	}

}

2023-11-15 20:34:24

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1302904.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

分布式搜索引擎02

分布式搜索引擎02 在昨天的学习中&#xff0c;我们已经导入了大量数据到elasticsearch中&#xff0c;实现了elasticsearch的数据存储功能。但elasticsearch最擅长的还是搜索和数据分析。 所以今天&#xff0c;我们研究下elasticsearch的数据搜索功能。我们会分别使用DSL和Res…

小红书笔记种草表现怎么看,营销攻略!

小红书平台的传播&#xff0c;离不开内容种草。当我们撰写好一篇笔记并进行发布后&#xff0c;该如何衡量这篇笔记的种草表现呢?今天我们为大家分享下小红书笔记种草表现怎么看&#xff0c;营销攻略&#xff01; 一、小红书笔记种草衡量指标 想要了解小红书笔记种草表现怎么看…

智能监控平台/视频共享融合系统EasyCVR接入大华SDK后只有一路通道可云台控制该如何解决?

TSINGSEE青犀视频监控汇聚平台EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安防视频监控的能力&…

31、卷积 - 参数 dilation 以及空洞卷积

在卷积算法中,还有一个不常见的参数叫做dilation(中文:膨胀)。 很多同学可能没听说过这个参数,下面看看这个参数有什么作用,用来控制什么的。 我们还是放这个经典的卷积运算图,图中是看不出 dilation 这个参数的存在的。 如果再换一张图呢,发现两图的区别了吗? 没错…

蓝桥杯Web组学习总结 - 目录导航版

HTML5 HTML 基础标签 HTML5 标签列表 HTML5 新特性 HTML5都有哪些新特性&#xff1f; CSS3 CSS 基础语法 CSS参考手册 盒子模型 CSS Box Model (盒子模型) 浮动与定位&#xff1f;&#xff1f; CSS 浮动(float)与定位(position) CSS布局之浮动和定位 CSS3 新特性 …

Python从入门到精通五:Python数据容器

数据容器入门 为什么学习数据容器 思考一个问题&#xff1a;如果我想要在程序中&#xff0c;记录5名学生的信息&#xff0c;如姓名。 如何做呢&#xff1f; 学习数据容器&#xff0c;就是为了批量存储或批量使用多份数据 Python中的数据容器&#xff1a; 一种可以容纳多份…

【每日一题】—— B. StORage room(Codeforces Round 912 (Div. 2))(位操作符)

&#x1f30f;博客主页&#xff1a;PH_modest的博客主页 &#x1f6a9;当前专栏&#xff1a;每日一题 &#x1f48c;其他专栏&#xff1a; &#x1f534; 每日反刍 &#x1f7e1; C跬步积累 &#x1f7e2; C语言跬步积累 &#x1f308;座右铭&#xff1a;广积粮&#xff0c;缓称…

LeetCode5.最长回文子串

昨天和之前打比赛的队友聊天&#xff0c;他说他面百度面到这道算法题&#xff0c;然后他用暴力法解的&#xff0c;面试官让他优化他没优化出来&#xff0c;这道题我之前没写过&#xff0c;我就想看看我能不能用效率高一点的方法把它做出来&#xff0c;我一开始就在想用递归或者…

排程系统中关于任务优先级的需求延伸与设计构思

无论是面向销售订单的MPS&#xff0c;还是基于多工序制约关系的APS&#xff0c;还是具体车间生产中针对单一工序的任务作业调度优化&#xff0c;都存在基于被排程对象(例如销售订单、生产工单、工序任务)的优先级进行优化的需求场景。当我们仅在宏观、较高层次的角度考虑&#…

Linux高级管理-基于域名的虚拟Web主机搭建

客服机限制地址 通过 Require 配置项&#xff0c;可以根据主机的主机名或P地址来决定是否允许客户端访问。在httpd服 务器的主配置文件的<Location>&#xff0c;<Directory>、<Files>、<Limit>配置段中均可以使用Require 配置 项来控制客户端的访问。使…

Python常见面试知识总结(二):数据结构、类方法及异常处理

【十三】Python中assert的作用&#xff1f; Python中assert&#xff08;断言&#xff09;用于判断一个表达式&#xff0c;在表达式条件为 f a l s e false false的时候触发异常。 断言可以在条件不满足程序运行的情况下直接返回错误&#xff0c;而不必等待程序运行后出现崩溃…

openGauss学习笔记-153 openGauss 数据库运维-备份与恢复-物理备份与恢复之gs_probackup

文章目录 openGauss学习笔记-153 openGauss 数据库运维-备份与恢复-物理备份与恢复之gs_probackup153.1 背景信息153.2 前提条件153.3 限制说明153.4 命令说明153.5 参数说明153.6 备份流程153.7 故障处理 openGauss学习笔记-153 openGauss 数据库运维-备份与恢复-物理备份与恢…

java.net.SocketException: Connection reset

背景 在我用socket进行TCP通信的时候&#xff0c;当我关闭client端时在服务端出现了Connection reset的异常。 一、问题 下面是异常信息&#xff1a; Exception in thread "Thread-12" java.lang.RuntimeException: java.net.SocketException: Connection reseta…

【复现】AnimateDiff复现过程记录

出现了非常多的问题&#xff0c;痛苦 1.首先是下载基础模型 手动下载所有模型并创建目录放到对应的地方 文件路径问题是最简单也是最麻烦的&#xff0c;简单是只要把原来指向huggingface和openai之类要联网下载的路径改为存放文件的本地路径就行&#xff0c;一定要是绝对路径&…

【计算机网络】UDP报文详解

目录 一. UDP协议概述 二. UDP报文格式 首部 三. UDP的缓冲区 一. UDP协议概述 UDP——用户数据报协议&#xff0c;是传输层的一个重要协议 基于UDP的应用层协议有&#xff1a;DNS&#xff0c;TFTP&#xff0c;SNMP&#xff0c;NTP 协议全称默认端口号DNSDomain Name Se…

idea__SpringBoot微服务09——员工管理系统,(Springboot解决乱码),thymeleaf语法,404页面。

员工管理系统 完整项目地址&#xff1a;一、首页实现&#xff08;注意的点&#xff09;二、国际化三、乱码解决四、登录功能实现&#xff08;注意的点&#xff09;五、登录拦截器&#xff08;注意的点&#xff09;六、展示员工列表&#xff08;注意的点&#xff09;1、前端页面…

HCIA-H12-811题目解析(9)

1、【单选题】下面选项中&#xff0c;能使一台IP地址为10.0.0.1的主机访问Interne的必要技术是&#xff1f; 2、【单选题】 FTP协议控制平面使用的端口号为&#xff1f; 3、【单选题】 使用FTP进行文件传输时&#xff0c;会建立多少个TCP连接&#xff1f; 4、【单选题】完成…

18.Java程序设计-基于Springboot的电影院售票系统的设计与实现

摘要 电影产业在当今社会中占据着重要地位&#xff0c;电影院作为观影的主要场所&#xff0c;其售票系统的高效性和用户体验至关重要。本文基于Spring Boot框架设计并实现了一款电影院售票系统&#xff0c;旨在提高售票效率、优化用户体验&#xff0c;并解决传统售票方式存在的…

堪比Postman!实用IDEA插件推荐

Postman是大家最常用的API调试工具&#xff0c;那么有没有一种方法可以不用手动写入接口到Postman&#xff0c;即可进行接口调试操作&#xff1f;今天给大家推荐一款IDEA插件&#xff1a;Apipost Helper&#xff0c;写完代码就可以调试接口并一键生成接口文档&#xff01;而且还…

我的NPI项目之Android 显示 -- 背光的电路小知识

由于使用的高通平台一直在演化&#xff0c;从SDM660,QCM4290,QCM4490再到QCM6490。产品的背光设计也是一直在迭代。 简单罗列了一下所经历的一些设计&#xff0c;简单的背光也涉及到了很多学问。 先说有哪些类型&#xff1a; 1. SDM660上由PMIC提供了wled给背光, 透过驱动直…