class087 动态规划中根据数据量猜解法的技巧【算法】

这篇具有很好参考价值的文章主要介绍了class087 动态规划中根据数据量猜解法的技巧【算法】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

class087 动态规划中根据数据量猜解法的技巧【算法】

2023-12-24 14:36:06

算法讲解087【必备】动态规划中根据数据量猜解法的技巧

class087 动态规划中根据数据量猜解法的技巧【算法】,左程云算法,算法,动态规划

code1 打 怪 兽

// 贿赂怪兽
// 开始时你的能力是0,你的目标是从0号怪兽开始,通过所有的n只怪兽
// 如果你当前的能力小于i号怪兽的能力,则必须付出b[i]的钱贿赂这个怪兽
// 然后怪兽就会加入你,他的能力a[i]直接累加到你的能力上
// 如果你当前的能力大于等于i号怪兽的能力,你可以选择直接通过,且能力不会下降
// 但你依然可以选择贿赂这个怪兽,然后怪兽的能力直接累加到你的能力上
// 返回通过所有的怪兽,需要花的最小钱数
// 测试链接 : https://www.nowcoder.com/practice/736e12861f9746ab8ae064d4aae2d5a9
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下的code,提交时请把类名改成"Main",可以直接通过

进行如下的思考:
假设a[i]数值的范围很大,但是b[i]数值的范围不大,该怎么做?
假设a[i]数值的范围不大,但是b[i]数值的范围很大,又该怎么做?

比如数据规模是:
1000只坏兽
能力1~104
钱1~10

dp[i][j]:1…i,花的的钱<=j,不能通过-无穷,能通过最大能力值
返回最后一行第一个不是-无穷的j即可
大小dp[1000][1000*10]

i号怪兽的能力a[i],钱b[i]
1)不贿赂i号怪兽: dp[i-1][j],条件:dp[i-1][j]>=a[i]
2)贿赂i号怪兽: dp[i-1][j-b[i]]+a[i],条件:j-b[i]>=0&&dp[i-1][j-b[i]]!=-无穷

比如数据规模是:
能力不大,钱很大

dp[i][j]:1…i号怪兽,通过能力正好是j的最小钱数
如果能通过就是最小的钱,如果不能通过就是+无穷
1)不贿赂i号怪兽: dp[i-1][j],条件:j>=a[i]&& dp[i-1][j]!=+无穷
2)贿赂i号怪兽: dp[i-1][j-a[i]]+b[i],条件:j-a[i]>=0&&dp[i-1][j-a[i]]!=+无穷
初始化dp[0][1…m]=+无穷
返回最后一行最小的值

package class087;

// 贿赂怪兽
// 开始时你的能力是0,你的目标是从0号怪兽开始,通过所有的n只怪兽
// 如果你当前的能力小于i号怪兽的能力,则必须付出b[i]的钱贿赂这个怪兽
// 然后怪兽就会加入你,他的能力a[i]直接累加到你的能力上
// 如果你当前的能力大于等于i号怪兽的能力,你可以选择直接通过,且能力不会下降
// 但你依然可以选择贿赂这个怪兽,然后怪兽的能力直接累加到你的能力上
// 返回通过所有的怪兽,需要花的最小钱数
// 测试链接 : https://www.nowcoder.com/practice/736e12861f9746ab8ae064d4aae2d5a9
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下的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;

public class Code01_BuyMonster {

	// 讲解本题的目的不仅仅是为了通过这个题,而是进行如下的思考:
	// 假设a[i]数值的范围很大,但是b[i]数值的范围不大,该怎么做?
	// 假设a[i]数值的范围不大,但是b[i]数值的范围很大,又该怎么做?

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

	// 假设a[i]数值的范围很大,但是b[i]数值的范围不大
	// 时间复杂度O(n * 所有怪兽的钱数累加和)
	public static int compute1(int n, int[] a, int[] b) {
		int m = 0;
		for (int money : b) {
			m += money;
		}
		// dp[i][j] : 花的钱不能超过j,通过前i个怪兽,最大能力是多少
		// 如果dp[i][j] == Integer.MIN_VALUE
		// 表示花的钱不能超过j,无论如何都无法通过前i个怪兽
		int[][] dp = new int[n + 1][m + 1];
		for (int i = 1; i <= n; i++) {
			for (int j = 0; j <= m; j++) {
				dp[i][j] = Integer.MIN_VALUE;
				if (dp[i - 1][j] >= a[i]) {
					dp[i][j] = dp[i - 1][j];
				}
				if (j - b[i] >= 0 && dp[i - 1][j - b[i]] != Integer.MIN_VALUE) {
					dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - b[i]] + a[i]);
				}
			}
		}
		int ans = -1;
		for (int j = 0; j <= m; j++) {
			if (dp[n][j] != Integer.MIN_VALUE) {
				ans = j;
				break;
			}
		}
		return ans;
	}

	// 就是方法1的空间压缩版本
	public static int compute2(int n, int[] a, int[] b) {
		int m = 0;
		for (int money : b) {
			m += money;
		}
		int[] dp = new int[m + 1];
		for (int i = 1, cur; i <= n; i++) {
			for (int j = m; j >= 0; j--) {
				cur = Integer.MIN_VALUE;
				if (dp[j] >= a[i]) {
					cur = dp[j];
				}
				if (j - b[i] >= 0 && dp[j - b[i]] != Integer.MIN_VALUE) {
					cur = Math.max(cur, dp[j - b[i]] + a[i]);
				}
				dp[j] = cur;
			}
		}
		int ans = -1;
		for (int j = 0; j <= m; j++) {
			if (dp[j] != Integer.MIN_VALUE) {
				ans = j;
				break;
			}
		}
		return ans;
	}

	// 假设a[i]数值的范围不大,但是b[i]数值的范围很大
	// 时间复杂度O(n * 所有怪兽的能力累加和)
	public static int compute3(int n, int[] a, int[] b) {
		int m = 0;
		for (int ability : a) {
			m += ability;
		}
		// dp[i][j] : 能力正好是j,并且确保能通过前i个怪兽,需要至少花多少钱
		// 如果dp[i][j] == Integer.MAX_VALUE
		// 表示能力正好是j,无论如何都无法通过前i个怪兽
		int[][] dp = new int[n + 1][m + 1];
		for (int j = 1; j <= m; j++) {
			dp[0][j] = Integer.MAX_VALUE;
		}
		for (int i = 1; i <= n; i++) {
			for (int j = 0; j <= m; j++) {
				dp[i][j] = Integer.MAX_VALUE;
				if (j >= a[i] && dp[i - 1][j] != Integer.MAX_VALUE) {
					dp[i][j] = dp[i - 1][j];
				}
				if (j - a[i] >= 0 && dp[i - 1][j - a[i]] != Integer.MAX_VALUE) {
					dp[i][j] = Math.min(dp[i][j], dp[i - 1][j - a[i]] + b[i]);
				}
			}
		}
		int ans = Integer.MAX_VALUE;
		for (int j = 0; j <= m; j++) {
			ans = Math.min(ans, dp[n][j]);
		}
		return ans == Integer.MAX_VALUE ? -1 : ans;
	}

	// 就是方法3的空间压缩版本
	public static int compute4(int n, int[] a, int[] b) {
		int m = 0;
		for (int ability : a) {
			m += ability;
		}
		int[] dp = new int[m + 1];
		for (int j = 1; j <= m; j++) {
			dp[j] = Integer.MAX_VALUE;
		}
		for (int i = 1, cur; i <= n; i++) {
			for (int j = m; j >= 0; j--) {
				cur = Integer.MAX_VALUE;
				if (j >= a[i] && dp[j] != Integer.MAX_VALUE) {
					cur = dp[j];
				}
				if (j - a[i] >= 0 && dp[j - a[i]] != Integer.MAX_VALUE) {
					cur = Math.min(cur, dp[j - a[i]] + b[i]);
				}
				dp[j] = cur;
			}
		}
		int ans = Integer.MAX_VALUE;
		for (int j = 0; j <= m; j++) {
			ans = Math.min(ans, dp[j]);
		}
		return ans == Integer.MAX_VALUE ? -1 : ans;
	}

}

code2 选择k个数字使得两集合累加和相差不超过1

// 选择k个数字使得两集合累加和相差不超过1
// 给定一个正数n,表示1~n这些数字都可以选择
// 给定一个正数k,表示要从1~n中选择k个数字组成集合A,剩下数字组成集合B
// 希望做到集合A和集合B的累加和相差不超过1
// 如果能做到,返回集合A选择了哪些数字,任何一种方案都可以
// 如果不能做到,返回长度为0的数组
// 2 <= n <= 10^6
// 1 <= k <= n
// 来自真实大厂笔试,没有测试链接,用对数器验证

1~n的和:sum
A:sum/2,sum/2+1(sum是奇数)

01背包
//1…i个数,还剩k个,还剩sum累加和
f(i,k,sum)

巧解
前k个数 中间的数 后k个数
构成sum

package class087;

// 选择k个数字使得两集合累加和相差不超过1
// 给定一个正数n,表示1~n这些数字都可以选择
// 给定一个正数k,表示要从1~n中选择k个数字组成集合A,剩下数字组成集合B
// 希望做到集合A和集合B的累加和相差不超过1
// 如果能做到,返回集合A选择了哪些数字,任何一种方案都可以
// 如果不能做到,返回长度为0的数组
// 2 <= n <= 10^6
// 1 <= k <= n
// 来自真实大厂笔试,没有测试链接,用对数器验证
public class Code02_PickNumbersClosedSum {

	// 正式方法
	// 最优解
	public static int[] pick(int n, int k) {
		long sum = (n + 1) * n / 2;
		int[] ans = generate(sum / 2, n, k);
		if (ans.length == 0 && (sum & 1) == 1) {
			ans = generate(sum / 2 + 1, n, k);
		}
		return ans;
	}

	// 1 ~ n这些数字挑选k个
	// 能不能凑够累加和sum
	// 能的话,返回挑选了哪些数字
	// 不能的话,返回长度为0的数组
	public static int[] generate(long sum, int n, int k) {
		long minKSum = (k + 1) * k / 2;
		int range = n - k;
		if (sum < minKSum || sum > minKSum + (long) range * k) {
			return new int[0];
		}
		// 100 15 -> 85
		long need = sum - minKSum;
		int rightSize = (int) (need / range);
		int midIndex = (k - rightSize) + (int) (need % range);
		int leftSize = k - rightSize - (need % range == 0 ? 0 : 1);
		int[] ans = new int[k];
		for (int i = 0; i < leftSize; i++) {
			ans[i] = i + 1;
		}
		if (need % range != 0) {
			ans[leftSize] = midIndex;
		}
		for (int i = k - 1, j = 0; j < rightSize; i--, j++) {
			ans[i] = n - j;
		}
		return ans;
	}

	// 为了验证
	// 检验得到的结果是否正确
	public static boolean pass(int n, int k, int[] ans) {
		if (ans.length == 0) {
			if (canSplit(n, k)) {
				return false;
			} else {
				return true;
			}
		} else {
			if (ans.length != k) {
				return false;
			}
			int sum = (n + 1) * n / 2;
			int pickSum = 0;
			for (int num : ans) {
				pickSum += num;
			}
			return Math.abs(pickSum - (sum - pickSum)) <= 1;
		}
	}

	// 记忆化搜索
	// 不是最优解,只是为了验证
	// 返回能不能做到
	public static boolean canSplit(int n, int k) {
		int sum = (n + 1) * n / 2;
		int wantSum = (sum / 2) + ((sum & 1) == 0 ? 0 : 1);
		int[][][] dp = new int[n + 1][k + 1][wantSum + 1];
		return f(n, 1, k, wantSum, dp);
	}

	public static boolean f(int n, int i, int k, int s, int[][][] dp) {
		if (k < 0 || s < 0) {
			return false;
		}
		if (i == n + 1) {
			return k == 0 && s == 0;
		}
		if (dp[i][k][s] != 0) {
			return dp[i][k][s] == 1;
		}
		boolean ans = f(n, i + 1, k, s, dp) || f(n, i + 1, k - 1, s - i, dp);
		dp[i][k][s] = ans ? 1 : -1;
		return ans;
	}

	// 为了验证
	// 对数器
	public static void main(String[] args) {
		int N = 60;
		int testTime = 5000;
		System.out.println("测试开始");
		for (int i = 0; i < testTime; i++) {
			int n = (int) (Math.random() * N) + 2;
			int k = (int) (Math.random() * n) + 1;
			int[] ans = pick(n, k);
			if (!pass(n, k, ans)) {
				System.out.println("出错了!");
			}
		}
		System.out.println("测试结束");
	}

}

code3 P1439 【模板】最长公共子序列

// 两个排列的最长公共子序列长度
// 给出由1~n这些数字组成的两个排列
// 求它们的最长公共子序列长度
// n <= 10^5
// 测试链接 : https://www.luogu.com.cn/problem/P1439
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下的code,提交时请把类名改成"Main",可以直接通过

如果是字符串的话,是二维动态规划

排列:
求出第二排列在第一个排列的下标数组,
再求出这个下标数组的最长递增子序列

package class087;

// 两个排列的最长公共子序列长度
// 给出由1~n这些数字组成的两个排列
// 求它们的最长公共子序列长度
// n <= 10^5
// 测试链接 : https://www.luogu.com.cn/problem/P1439
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下的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;

public class Code03_PermutationLCS {

	public static int MAXN = 100001;

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

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

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

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

	public static int 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) {
			n = (int) in.nval;
			for (int i = 0; i < n; i++) {
				in.nextToken();
				a[i] = (int) in.nval;
			}
			for (int i = 0; i < n; i++) {
				in.nextToken();
				b[i] = (int) in.nval;
			}
			out.println(compute());
		}
		out.flush();
		out.close();
		br.close();
	}

	public static int compute() {
		for (int i = 0; i < n; i++) {
			where[a[i]] = i;
		}
		for (int i = 0; i < n; i++) {
			b[i] = where[b[i]];
		}
		return lis();
	}

	// 讲解072 - 最长递增子序列及其扩展
	public static int lis() {
		int len = 0;
		for (int i = 0, find; i < n; i++) {
			find = bs(len, b[i]);
			if (find == -1) {
				ends[len++] = b[i];
			} else {
				ends[find] = b[i];
			}
		}
		return len;
	}

	public static int bs(int len, int num) {
		int l = 0, r = len - 1, m, ans = -1;
		while (l <= r) {
			m = (l + r) / 2;
			if (ends[m] >= num) {
				ans = m;
				r = m - 1;
			} else {
				l = m + 1;
			}
		}
		return ans;
	}

}

code4 1187. 使数组严格递增

// 使数组严格递增的最小操作数
// 给你两个整数数组 arr1 和 arr2
// 返回使 arr1 严格递增所需要的最小操作数(可能为0)
// 每一步操作中,你可以分别从 arr1 和 arr2 中各选出一个索引
// 分别为 i 和 j,0 <= i < arr1.length 和 0 <= j < arr2.length
// 然后进行赋值运算 arr1[i] = arr2[j]
// 如果无法让 arr1 严格递增,请返回-1
// 1 <= arr1.length, arr2.length <= 2000
// 0 <= arr1[i], arr2[i] <= 10^9
// 测试链接 : https://leetcode.cn/problems/make-array-strictly-increasing/

处理arr2数组,使之变成严格递增不重复的数组
要替换更大的,第一个大选最合适的那个就好了

f(i)
0…i-1是严格递增的,并且i-1是原装的,把0…n-1变成严格递增的
i…后面的数组,要替换多少个

枚举每一个下一个原装位置
那么中间不用原装的数,替换为arr[2]中刚大的那些数
并且要判断可行性原装位置合适吗

package class087;

import java.util.Arrays;

// 使数组严格递增的最小操作数
// 给你两个整数数组 arr1 和 arr2
// 返回使 arr1 严格递增所需要的最小操作数(可能为0)
// 每一步操作中,你可以分别从 arr1 和 arr2 中各选出一个索引
// 分别为 i 和 j,0 <= i < arr1.length 和 0 <= j < arr2.length
// 然后进行赋值运算 arr1[i] = arr2[j]
// 如果无法让 arr1 严格递增,请返回-1
// 1 <= arr1.length, arr2.length <= 2000
// 0 <= arr1[i], arr2[i] <= 10^9
// 测试链接 : https://leetcode.cn/problems/make-array-strictly-increasing/
public class Code04_MakeArrayStrictlyIncreasing {

	public static int makeArrayIncreasing1(int[] arr1, int[] arr2) {
		Arrays.sort(arr2);
		int m = 1;
		for (int i = 1; i < arr2.length; i++) {
			if (arr2[i] != arr2[m - 1]) {
				arr2[m++] = arr2[i];
			}
		}
		int n = arr1.length;
		int[] dp = new int[n];
		Arrays.fill(dp, -1);
		int ans = f1(arr1, arr2, n, m, 0, dp);
		return ans == Integer.MAX_VALUE ? -1 : ans;
	}

	// arr1长度为n,arr2有效部分长度为m
	// arr2有效部分可以替换arr1中的数字
	// arr1[0..i-1]已经严格递增且arr1[i-1]一定没有替换
	// 返回让arr1整体都严格递增,arr1[i...]范围上还需要几次替换
	// 如果做不到,返回无穷大
	public static int f1(int[] arr1, int[] arr2, int n, int m, int i, int[] dp) {
		if (i == n) {
			return 0;
		}
		if (dp[i] != -1) {
			return dp[i];
		}
		// ans : 遍历所有的分支,所得到的最少的操作次数
		int ans = Integer.MAX_VALUE;
		// pre : 前一位的数字
		int pre = i == 0 ? Integer.MIN_VALUE : arr1[i - 1];
		// find : arr2有效长度m的范围上,找到刚比pre大的位置
		int find = bs(arr2, m, pre);
		// 枚举arr1[i...]范围上,第一个不需要替换的位置j
		for (int j = i, k = 0, next; j <= n; j++, k++) {
			if (j == n) {
				ans = Math.min(ans, k);
			} else {
				// pre : 被arr2替换的前一位数字
				if (pre < arr1[j]) {
					next = f1(arr1, arr2, n, m, j + 1, dp);
					if (next != Integer.MAX_VALUE) {
						ans = Math.min(ans, k + next);
					}
				}
				if (find != -1 && find < m) {
					pre = arr2[find++];
				} else {
					break;
				}
			}
		}
		dp[i] = ans;
		return ans;
	}

	// arr2[0..size-1]范围上是严格递增的
	// 找到这个范围上>num的最左位置
	// 不存在返回-1
	public static int bs(int[] arr2, int size, int num) {
		int l = 0, r = size - 1, m;
		int ans = -1;
		while (l <= r) {
			m = (l + r) / 2;
			if (arr2[m] > num) {
				ans = m;
				r = m - 1;
			} else {
				l = m + 1;
			}
		}
		return ans;
	}

	// 严格位置依赖的动态规划
	// 和方法1的思路没有区别
	// 甚至填写dp表的逻辑都保持一致
	public static int makeArrayIncreasing2(int[] arr1, int[] arr2) {
		Arrays.sort(arr2);
		int m = 1;
		for (int i = 1; i < arr2.length; i++) {
			if (arr2[i] != arr2[m - 1]) {
				arr2[m++] = arr2[i];
			}
		}
		int n = arr1.length;
		int[] dp = new int[n + 1];
		for (int i = n - 1, ans, pre, find; i >= 0; i--) {
			ans = Integer.MAX_VALUE;
			pre = i == 0 ? Integer.MIN_VALUE : arr1[i - 1];
			find = bs(arr2, m, pre);
			for (int j = i, k = 0, next; j <= n; j++, k++) {
				if (j == n) {
					ans = Math.min(ans, k);
				} else {
					if (pre < arr1[j]) {
						next = dp[j + 1];
						if (next != Integer.MAX_VALUE) {
							ans = Math.min(ans, k + next);
						}
					}
					if (find != -1 && find < m) {
						pre = arr2[find++];
					} else {
						break;
					}
				}
			}
			dp[i] = ans;
		}
		return dp[0] == Integer.MAX_VALUE ? -1 : dp[0];
	}

}

2023-12-24 16:38:16文章来源地址https://www.toymoban.com/news/detail-763721.html

到了这里,关于class087 动态规划中根据数据量猜解法的技巧【算法】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 力扣70. 爬楼梯(动态规划 Java,C++解法)

    Problem: 70. 爬楼梯 由于本题目中第i层台阶只能由于第 i- 1 层台阶和第 i-2 层台阶走来,所以可以联想到动态规划,具体如下: 1.定义多阶段决策模型:对于每一上台阶看作一种状态; 2.定义状态转移方程:int[] dp = new int[n + 1] 用于记录第i个台阶可以走到的走法 ;dp[i] = dp[i -

    2024年01月20日
    浏览(45)
  • 数字三角形-蓝桥杯真题动态规划PYTHON解法

    目录 题目描述  解题思路 DP初始化 DP最终条件 DP初始条件 题目限制条件 总代码 首先映入我们眼帘的就是一个三角形,加求路径和最大,类似于找最短路径的题,很明显是一个二维数组的动态规划问题,对于动态规划问题我们只需要找好最终条件,初始条件(也就是特殊条件

    2024年02月09日
    浏览(39)
  • 53.最大子数组和(前缀和、动态规划,C解法)

    给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 子数组 是数组中的一个连续部分。 示例 1: 示例 2: 示例 3: 分析:         前缀和数组可以在单位时间内得到 [ l , r ] 区间和,但本题所求子数组中 l 、 r 以

    2024年01月22日
    浏览(50)
  • 算法导论-分而治之-最大子数组(含动态规划解法)

    分治法 把一个问题分成(同类的)几个子问题。 递归地解决(征服)每个子问题。 将子问题的解决方案组合成整体解决方案。 通常的使用 将大小为n的问题分成两个大小为n / 2的子问题。 递归求解(攻克)两个子问题。 将两个方案组合成整体方案。 当子问题足够大以进行递归求解

    2024年02月03日
    浏览(42)
  • 【动态规划之完全背包问题】完全背包问题的通用解法与优化

    ⭐️ 前面的话 ⭐️ 本篇文章将介绍动态规划中的背包问题——完全背包问题,前面我们已经介绍了0-1背包问题,其实完全背包问题就只改了0-1背包问题的一个条件,即物品可选择次数由一次改为无数次,仅此而已,下面我们就来开始介绍完全背包问题。 📒博客主页:未见

    2023年04月22日
    浏览(50)
  • 从01背包开始动态规划:暴力解法 + dp + 滚动数组 + dp优化

        01背包问题是动态规划中最经典的问题之一,本篇将通过给出其四种解法,使读者更深刻理解动态规划。   有N件物品和一个容量是 V 的背包,每个物品有各自的体积和价值,且每个物品只能放一次(这也是01背包名字的由来),如何让背包里装入的物品具有最大的价值总

    2024年04月17日
    浏览(54)
  • class066 一维动态规划【算法】

    算法讲解066【必备】从递归入手一维动态规划 // 斐波那契数 // 斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 // 该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。 // 也就是:F(0) = 0,F(1) = 1 // F(n) = F(n - 1) + F(n - 2),其中 n 1 // 给定 n ,请计算

    2024年02月04日
    浏览(35)
  • 2525.根据规则将箱子分类/并查集/动态规划

    2525. 根据规则将箱子分类 - 力扣(LeetCode) 给你四个整数  length  , width  , height  和  mass  ,分别表示一个箱子的三个维度和质量,请你返回一个表示箱子  类别  的字符串。 如果满足以下条件,那么箱子是  \\\"Bulky\\\"  的: 箱子  至少有一个  维度大于等于  104  。 或者

    2024年02月07日
    浏览(43)
  • 【算法&数据结构体系篇class24】:滑动窗口技巧

    滑动窗口是一种想象出来的数据结构: 滑动窗口有左边界L和有边界R 在数组或者字符串或者一个序列上,记为S,窗口就是S[L..R]这一部分 L往右滑意味着一个样本出了窗口,R往右滑意味着一个样本进了窗口 L和R都只能往右滑 窗口不管L还是R滑动之后,都会让窗口呈现新状况,

    2023年04月09日
    浏览(58)
  • 根据渲染数据长度动态渲染后缀图标

    在动态获取数据时,想要渲染后面的图标是根据数据的长度渲染图标位置,效果如下:  代码如下: 

    2024年02月13日
    浏览(73)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包