思路一:
直接记忆化搜索(遇见不会的 dp 先记忆化搜索了再说(记忆化过不了就是需要优化))
#include <bits stdc++.h>
using namespace std;
int n, m;
vector<pii> ve[69];
int dp[100][42009], vis[100][42009];
int dfs(int x, int s) {
if(s < 0) return intmax + 1;
if(x > m) return 0;
if(vis[x][s] ++) return dp[x][s]; /// 防止进入后续递归过程
if(ve[x].size() == 0) return dp[x][s] = dfs(x + 1, s);
int ans = max(dfs(x + 1, s), dfs(x + 1, s - ve[x][0].first) + ve[x][0].second); /// 不取 /// 取一个
if(ve[x].size() > 1)
ans = max(ans, dfs(x + 1, s - ve[x][0].first - ve[x][1].first) + ve[x][0].second + ve[x][1].second); /// 取两个
if(ve[x].size() > 2) {
ans = max(ans, dfs(x + 1, s - ve[x][0].first - ve[x][2].first) + ve[x][0].second + ve[x][2].second); /// 取两个
ans = max(ans, dfs(x + 1, s - ve[x][0].first - ve[x][1].first - ve[x][2].first)
+ ve[x][0].second + ve[x][1].second + ve[x][2].second); /// 取三个
}
return dp[x][s] = ans;
}
int main() {
cin >> n >> m;
int x, y, z;
for(int i = 1; i <= m; i ++) {
cin >> x >> y >> z;
if(z) ve[z].push_back({x, y * x});
else ve[i].push_back({x, y * x});
}
cout << dfs(1, n);
return 0;
}思路二:
此题非常像 01背包 问题,只是由于附件必须依赖于主件所有使得问题麻烦了起来
- 对于不同类物品之间不存在依赖关系,故可以直接用 01背包 的方式 dp
- 对于同类物品之间,我们使用 附件+主件 的格式将物品组合在一起考虑
- 那么我们只能在这一类物品之间选择一个,这个子问题可以通过 dp 解决(分类进行 01背包 递推后对于同一类物品使用相同的递推方程,转移过程来源与之前同类物品的来源相似(不要忘记对自己也要进行一个判断))
#include <bits stdc++.h>
using namespace std;
const int N = 32009, M = 69;
int n, m;
vector<pii> ve[M];
int dp[M][N];
int main() {
cin >> n >> m;
int v, w, q;
for(int i = 1; i <= m; i ++) {
cin >> v >> w >> q; w *= v;
if(q) {
int x = ve[q].size();
for(int j = 0; j < x; j ++) { /// 状态转移(构造最多四种选择状态)
ve[q].push_back({v + ve[q][j].first, w + ve[q][j].second});
}
} else ve[i].push_back({v, w});
}
for(int i = 1; i <= m; i ++) /// 注意,由于上面的转移,这条语句必须位于读入的后方
ve[i].push_back({0, 0}); /// 保证连续性(存在“不取”这种取法)
for(int i = m; i; i --) { /// 枚举类
int q = ve[i].size();
for(int j = 0; j < q; j ++) { /// 枚举同类中物品
v = ve[i][j].first, w = ve[i][j].second;
for(int k = n; k >= v; k --) { /// 枚举空间
dp[i][k] = max(dp[i][k], max(dp[i + 1][k - v] + w, dp[i + 1][k]));
}
}
}
cout << dp[1][n];
return 0;
}思考:
- 我们学习算法总是会选择“最优解”来“记忆“(有时甚至是背诵),但是这题正是基于没有空间优化的 01背包 进行的推导(当然上述代码还可以进行空间优化),但是 左神 告诉我们:每个算法都是一步一步慢慢来的!

京公网安备 11010502036488号