赞
踩
背包问题:有限制条件下的"组合问题"
你有一个背包,地上有一堆物品,挑选一些物品放入背包中
限制因素
当研究一个问题,出现选或者不选的情况,思路就可以往01背包上靠
注意:背包问题是必须要掌握的算法问题
确定状态表示 -> dp[i][j]
的含义
dp[i]
:从前i
个物品中选,所有选法中,能挑选出来的最大价值 ×
dp[i][j]
:从前i
个物品中挑选,总体积不超过j
,所有选法中,能挑选出来的最大价值dp[i][j]
:从前i
个物品中挑选,总体积恰好等于j
,所有选法中,能挑选出来的最大价值推导状态转移方程:根据最后一个位置的情况,分情况讨论
不要求恰好装满:j - v[i] >= 0
是为了确保背包此时容量足够塞下当前物品
要求恰好装满:dp[i][j] == -1
表示没有这种情况,即此时总体积凑不到j
初始化:
vector<vector<int>> dp(n + 1, vector<int>(V + 1))
-1
确定填表顺序:从上往下
确定返回值:
dp[n][V]
dp[n][V] == -1 ? 0 : dp[n][V]
每次填值,只依赖上一行的值
可以一个一维数组就优化掉此问题
操作
j
的遍历顺序注意:不要去强行解释优化后的妆台表示以及状态转移方程,费时费力还没啥意义
// v1.0 int main() { int n = 0, V = 0; cin >> n >> V; vector<int> v(n + 1), w(n + 1); for(int i = 1; i <= n; i++) { cin >> v[i] >> w[i]; } vector<vector<int>> dp(n + 1, vector<int>(V + 1)); // Q1 for(int i = 1; i <= n; i++) { for(int j = 1; j <= V; j++) { dp[i][j] = dp[i - 1][j]; if(j >= v[i]) { dp[i][j] = max(dp[i][j], dp[i - 1][j - v[i]] + w[i]); } } } cout << dp[n][V] << endl; // Q2 dp.resize(n + 1, vector<int>(V + 1)); for(int i = 1; i <= V; i++) { dp[0][i] = -1; } for(int i = 1; i <= n; i++) { for(int j = 1; j <= V; j++) { dp[i][j] = dp[i - 1][j]; if(j >= v[i] && dp[i - 1][j - v[i]] != -1) { dp[i][j] = max(dp[i][j], dp[i - 1][j - v[i]] + w[i]); } } } cout << (dp[n][V] == -1 ? 0 : dp[n][V]) << endl; return 0; } ----------------------------------------------------------------------------- // v2.0 滚动数组优化 int main() { int n = 0, V = 0; cin >> n >> V; vector<int> v(n + 1), w(n + 1); for(int i = 1; i <= n; i++) { cin >> v[i] >> w[i]; } vector<int> dp(V + 1); // Q1 for(int i = 1; i <= n; i++) { for(int j = V; j >= v[i]; j--) { dp[j] = max(dp[j], dp[j - v[i]] + w[i]); } } cout << dp[V] << endl; // Q2 dp.resize(V + 1, 0); for(int i = 1; i <= V; i++) { dp[i] = -1; } for(int i = 1; i <= n; i++) { for(int j = V; j >= v[i]; j--) { if(dp[j - v[i]] != -1) { dp[j] = max(dp[j], dp[j - v[i]] + w[i]); } } } cout << (dp[V] == -1 ? 0 : dp[V]) << endl; return 0; }
sum / 2
--> 01背包确定状态表示 -> dp[i][j]
的含义
dp[i]j]
:从前i
个数中**选**,所有的选法中,能否凑成j
这个数推导状态转移方程:根据最后一个位置的情况,分情况讨论
dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i]]
初始化:
确定填表顺序:从上往下
确定返回值:dp[n][sum / 2]
// v1.0 bool canPartition(vector<int>& nums) { int n = nums.size(), sum = 0; for(auto& x : nums) { sum += x; } if(sum % 2) return false; int aim = sum / 2; vector<vector<bool>> dp(n + 1, vector<bool>(aim + 1)); // Init for(int i = 1; i <= n; i++) { dp[i][0] = true; } // DP for(int i = 1; i <= n; i++) { for(int j = 1; j <= aim; j++) { dp[i][j] = dp[i - 1][j]; if(j >= nums[i - 1]) { dp[i][j] = dp[i][j] || dp[i - 1][j - nums[i - 1]]; } } } return dp[n][aim]; } ---------------------------------------------------------------------- // v2.0 滚动数组优化 bool canPartition(vector<int>& nums) { int n = nums.size(), sum = 0; for(auto& x : nums) { sum += x; } if(sum % 2) return false; int aim = sum / 2; vector<bool> dp(aim + 1); dp[0] = true; // DP for(int i = 1; i <= n; i++) { for(int j = aim; j >= nums[i - 1]; j--) { dp[j] = dp[j] || dp[j - nums[i - 1]]; } } return dp[aim]; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。