01背包问题是经典的题目,w[i]表示第i个物品重量,v[i]表示第i个物品价值,很容易想到在思考是,我们需要记录三种状态,当前背包装的数量,当前背包空间,当前背包装的总价值,我们用dfs(i, j)来表示,记录状态之后其余就是在这种情况下对剩下可选的物品进行装包,如果当前物品可装背包就可以得到一个递推公式 d f s ( i , j ) = { d f s ( i − 1 , j ) , j < w [ i ] m a x ( d f s ( i − 1 , j ) , d f s ( i , j − w [ i ] ) + v [ i ] ) , j > = w [ i ] dfs(i,\ j) = \begin{cases} dfs(i-1,\ j),\ j < w[i] \\ max( dfs(i-1,\ j),\ dfs(i,\ j - w[i]) \ + \ v[i]), j >= w[i] \end{cases} dfs(i,j)={dfs(i−1,j),j<w[i]max(dfs(i−1,j),dfs(i,j−w[i])+v[i]),j>=w[i]
#include <iostream>
#include <vector>
using namespace std;int main() {int m, n;cin >> m >> n;vector<int> w(m);vector<int> v(m);for(int i = 0; i < m; ++i) {cin >> w[i];}for(int i = 0; i < m; ++i) {cin >> v[i];}// 递归+记忆化 搜索,Kama网这种做法会内存超限vector<vector<int>> memo(m, vector<int>(n + 1, -1));auto dfs = [&](auto&& dfs, int i, int j) -> int {if(i < 0)return 0;int& res = memo[i][j];if(res != -1)return res;if(j < w[i]) {return res = dfs(dfs, i - 1, j);}return res = max(dfs(dfs, i - 1, j), dfs(dfs, i, j - w[i]) + v[i]);};//cout << dfs(dfs, m - 1, n) << endl; // 二维dp,将记忆化搜索转为递推,dfs(i, j) 使用数组dp[i][j]表示vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));for(int i = 1; i <= m; ++i) {for(int j = 1; j <= n; ++j) {if(j >= w[i-1]) {dp[i][j] = max(dp[i-1][j], dp[i-1][j - w[i-1]] + v[i-1]);} elsedp[i][j] = dp[i-1][j];}}// cout << dp[m][n] << endl; // 递推优化,f[n]表示n空间大小能够填充的最大价值,f[n] = max(f[n], f[n-w[i]] + v[i]);vector<int> f(n + 1, 0);for(int i = 0; i < m; ++i) {for(int j = n; j >= w[i]; --j) { // 从后往前遍历,防止选择重复f[j] = max(f[j], f[j - w[i]] + v[i]);}}cout << dp[n] << endl; return 0;
}
416. 分割等和子集
题目链接:416. 分割等和子集
思路:
根据题意,需要从数组中选取n个数,n个数的和要刚好是数组和的一半,思路和0 1 背包一致,数就是物品,数的值就是价值,使用dp[i][j] 表示从i个数选择的和为j的可能性,如此得到递推公式 d p [ i , j ] = { d p [ i − 1 , j ] , j < n u m [ i ] d p [ i − 1 , j ] ∣ ∣ d p [ i , j − n u m s [ i ] ] , j > = n u m s [ i ] dp[i,\ j] = \begin{cases} dp[i-1,\ j],\ j < num[i] \\ dp[i-1,\ j] \ || \ dp[i,\ j - nums[i]], j >= nums[i] \end{cases} dp[i,j]={dp[i−1,j],j<num[i]dp[i−1,j]∣∣dp[i,j−nums[i]],j>=nums[i] 当 j 比nums[i] 小时,nums[i]不可选故dp[i][j]的值就和dp[i-1][j]一致,j >= nums[i]时,可选可不选,故dp[i][j] = dp[i-1][j] || dp[i][j-nums[i]] 为二者的异或值
本题需要转换一下,由题意可知,数组中一部分数要转为负数,设选择过程中,数组和为sum,选择的正数和为 x,选择负数的绝对值和为 y,那么就有以下公式 x + y = s u m x − y = t a r g e t ⇓ { x = s u m + t a r g e t 2 y = s u m − t a r g e t 2 x + y = sum\\ x - y = target \\ \Downarrow \\ \begin{cases} x = \frac{sum \ +\ target}{2} \\ y = \frac{sum \ -\ target}{2} \\ \end{cases} x+y=sumx−y=target⇓{x=2sum+targety=2sum−target 故本题的思路是,选择x,y满足上述等式条件即可,知道sum和target,可以算出x,y,剩下的思路就是和分割等和子集类似了,不过这里的和是x或者y,然后这个过程记录组合数量即可
本题还需要一个注意的地方,就是target可以为正也可以为负 当target >= 0 时,选择 y = s u m − t a r g e t 2 y = \frac{sum \ -\ target}{2} y=2sum−target,这样选择y,递推过程就会变少 当target < 0 时, 选择 x = s u m + t a r g e t 2 x = \frac{sum \ +\ target}{2} x=2sum+target,这样选择x,递推过程就会变少 综上,选择 x = s u m − ∣ t a r g e t ∣ 2 x = \frac{sum \ -\ |target|}{2} x=2sum−∣target∣,不管target,为正还是负,都可以选择到最小的数。
思路分析完之后,递推公式和分割等和子集一样,不过这里不是记录可行性,记录组合数目,递推公式如下 d p [ i ] [ j ] = { d p [ i − 1 ] [ j ] , j < n u m [ i ] d p [ i − 1 ] [ j ] + d p [ i ] [ j − n u m s [ i ] ] , j > = n u m s [ i ] dp[i][j] = \begin{cases} dp[i-1][j],\ j < num[i] \\ dp[i-1][j] \ + \ dp[i][j - nums[i]], j >= nums[i] \end{cases} dp[i][j]={dp[i−1][j],j<num[i]dp[i−1][j]+dp[i][j−nums[i]],j>=nums[i]
代码
class Solution {
public:// 二维递推公式如下int f1(vector<int>& nums, int target) {int s = reduce(nums.begin(), nums.end()) - abs(target);if (s < 0 || s % 2) {return 0;}int m = s / 2; // 背包容量int n = nums.size();vector<vector<int>> dp(n + 1, vector<int>(m + 1));dp[0][0] = 1;for (int i = 0; i < n; i++) {for (int c = 0; c <= m; c++) {if (c < nums[i]) {dp[i + 1][c] = dp[i][c]; // 只能不选} else {dp[i + 1][c] = dp[i][c] + dp[i][c - nums[i]]; // 不选 + 选}}}return f[n][m];}// 二维递推优化,一维递推int findTargetSumWays(vector<int>& nums, int target) {int n = nums.size(), sum = reduce(nums.begin(), nums.end()) - abs(target);if(sum < 0 || sum % 2) // 判断可行性,不能整除2 带表没有符合题意的x 或者yreturn 0;int q = sum / 2; // 求出x 或 y 中的最小值// 优化一维递推 dp[sum] = dp[sum] + dp[sum - num[i]]vector<int> dp(q + 1);dp[0] = 1;for(int x : nums) {for(int j = q; j >= x; --j) {dp[j] += dp[j - x];}}return dp[q];}
};
2915. 和为目标值的最长子序列的长度
题目链接:2915. 和为目标值的最长子序列的长度
思路:
本题思路和分割等和子集那题类似,这找的是符合和为target的序列的最大长度,故使用dp[i][j]记录 长度 i 和为 j的最大子序列长度可得递推公式 d p [ i ] [ j ] = { d p [ i − 1 ] [ j ] , j < n u m [ i ] m a x ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − n u m s [ i ] ] + 1 ) , j > = n u m s [ i ] dp[i][j] = \begin{cases} dp[i-1][j],\ j < num[i] \\ max(dp[i-1][j] \ , \ dp[i][j - nums[i]] + 1), j >= nums[i] \end{cases} dp[i][j]={dp[i−1][j],j<num[i]max(dp[i−1][j],dp[i][j−nums[i]]+1),j>=nums[i]
Prof. Marc A. Rosen, Ontario Tech University, Canada
曾担任安大略省理工大学工程与应用科学学院创始院长、加拿大工程学院院长和加拿大机械工程学会会长。 他的主要研究领域是能源、热力学、可持续发展等。Google Scholar Citations 48000余次,H指数98。Prof. …
1.什么是Spring Cloud Data Flow?
Spring Cloud Data Flow 是一个用于构建和编排数据处理流水线的云原生框架。它提供了一种简化的方式来定义、部署和管理数据处理任务和流应用程序。以下是一些关键特性和组件:
关键特性 流处理: 支持实时数…