【Luogu】【关卡2-15】动态规划的背包问题(2017年10月)【还差一道题】
任务说明:这是最基础的动态规划。不过如果是第一次接触会有些难以理解。加油闯过这个坎。
01背包二维数组优化成滚动数组的时候有坑有坑有坑!!!必须要downto,downto,downto
情景和代码见装箱问题。
P1060 开心的金明
小明的妈妈给小明N元钱,小明想买m件物品,每个物品价值为 价格*重要度,求出不超过N元钱的情况下,最多能买多少价值的物品,输出价值。
解法:直接的01背包问题,我居然还去看了书。。递推方程一次写不出来。方程需要记忆。
dp[i][j]表示前i件物品总价格不超过j元的最大总价值。
需要学习下怎么在博客园输入latex公式orz。。。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <vector> #include <cstdio> #include <cstring> using namespace std; int main() { int n, m; cin >> n >> m; int w[m][2]; for (int i = 0; i < m; ++i) { cin >> w[i][0] >> w[i][1]; w[i][1] *= w[i][0]; } int dp[m+1][n+1]; memset(dp, 0, sizeof(dp)); for (int i = 1; i <= m; ++i) { for (int j = 1; j <= n; ++j) { if (w[i-1][0] > j) { dp[i][j] = dp[i-1][j]; } else { dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i-1][0]] + w[i-1][1]); } } } cout << dp[m][n] << endl; return 0; }
P1164 小A点菜
有M元, N种菜(每个菜只有一份),每种菜有价格,求能把M元全花完的方案数。
解答:一开始全WA....哭死..其实还是经典的01背包问题。
一维的转移方程为: dp[j] += dp[j-price[i-1]]; 初始化条件为 dp[0] = 1, dp[1..m] = 0。 (因为需要花0元只有一种方案,就是啥也不买)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int main() { 5 int n, m; 6 cin >> n >> m; 7 int dp[m+1] = {0}; 8 dp[0] = 1; 9 int price[n]; 10 for (int i = 0; i < n; ++i) { 11 cin >> price[i]; 12 } 13 for(int i = 1; i <= n; ++i) { 14 for (int j = m; j >= 0; --j) { 15 if (j >= price[i-1]) { 16 dp[j] = dp[j] + dp[j-price[i-1]]; 17 } 18 } 19 } 20 cout << dp[m] << endl; 21 return 0; 22 }
金明的预算方案
P1048 采药
就是01背包,裸的,直接写了,方程同P1060
提交一次AC
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <cstdlib> 2 #include <cstdio> 3 #include <iostream> 4 #include <cstring> 5 using namespace std; 6 7 int main() { 8 int T, M; 9 cin >> T >> M; 10 int w[M][2]; 11 for (int i = 0; i < M; ++i) { 12 cin >> w[i][0] >> w[i][1]; 13 } 14 int dp[M+1][T+1]; 15 memset(dp, 0, sizeof(dp)); 16 17 for (int i = 1; i <= M; ++i) { 18 for (int j = 1; j <= T; ++j) { 19 if (j < w[i-1][0]) { dp[i][j] = dp[i-1][j]; } 20 else { 21 dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i-1][0]] + w[i-1][1]); 22 } 23 } 24 } 25 cout << dp[M][T] << endl; 26 return 0; 27 }
P1049 装箱问题
有一个箱子容量为V(正整数,0<=V<=20000),同时有n个物品(0<n<=30,每个物品有一个体积(正整数)。 要求n个物品中,任取若干个装入箱内,使箱子的剩余空间为最小。
就是求怎么样是用的空间最大。裸的01背包。优化成滚动数组的时候被坑了。
//写成滚动数组的时候,max(dp[j], dp[j-w[i-1]]+w[i-1]);如果是从左到右,那么dp[j-w[i-1]]是个已经被更新过的值。。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <cstring> 5 6 using namespace std; 7 8 int main() { 9 int c, n; 10 cin >> c >> n; 11 int w[n+1]; 12 for (int i = 0; i < n; ++i) { 13 cin >> w[i]; 14 } 15 int dp[c+1] = {0}; 16 for (int i = 1; i <= n; ++i) { 17 //滚动数组这么写的时候,max(dp[j], dp[j-w[i-1]]+w[i-1]);如果是从左到右,那么dp[j-w[i-1]]是个已经被更新过的值。。 18 //for (int j = 1; j <= c; ++j) { 19 for (int j = c; j >= 1 ; --j) { 20 if (j < w[i-1]) { dp[j] = dp[j]; } 21 else { 22 dp[j] = max(dp[j], dp[j-w[i-1]]+w[i-1]); 23 } 24 } 25 } 26 cout << c - dp[c] << endl; 27 return 0; 28 }
P1616 疯狂的采药
采药问题的升级版,不限制每种物品的个数。完全背包问题(每个物品无穷个)。
完全背包的解法就是01背包的downto的写法改成正着写...不过这不是重点。重点是理解为啥需要正着写。
提交一次AC了
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 int main() { 6 int T, M; 7 cin >> T >> M; 8 int w[M][2]; 9 for (int i = 0; i < M; ++i) { 10 cin >> w[i][0] >> w[i][1]; 11 } 12 int dp[T+1] = {0}; 13 for (int i = 1; i <= M; ++i) { 14 for (int j = 1; j <= T; ++j) { 15 if (j < w[i-1][0]) { continue; } 16 else { 17 dp[j] = max(dp[j], dp[j-w[i-1][0]]+w[i-1][1]); 18 } 19 } 20 } 21 cout << dp[T] << endl; 22 return 0; 23 }