这是一道非常基础的01背包问题,背包容量就是时间,物品价值就是草药价值。
我们先来看二维的背包。状态很容易写出来,dp[i][j]就是前i种草药j时间内所能采到的最大价值。动态转移方程也就很容易写出,dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]],v[i]储存的是第i种草药的时间。第二种情况需要j>=v[i]。代码如下(时间和体积数组直接用对组储存):
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
typedef pair<int, int> PII;
int t, m;
int dp[110][1010];
PII h[110];
int main()
{
cin >> t >> m;
for (int i = 1; i <= m; i++)
cin >> h[i].first >> h[i].second;
for (int i = 1; i <= m; i++) {
for (int j = 0; j <= t; j++) {
dp[i][j] = dp[i - 1][j];
if (j >= h[i].first)
dp[i][j] = max(dp[i][j], dp[i - 1][j - h[i].first] + h[i].second);
}
}
cout << dp[m][t];
return 0;
}
当然,可以将其优化成一维数组。这里不同的是,第二层循环是从大到小。这里我给出两种不同的理解方式。我们先看二维的状态转移方程:dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]。如果将其改为一维,就是dp[j]=max(dp[j],dp[j-v[i]])。如果第二层循环是从小到大,dp[j-v[i]]用到的是第i层更新的结果,但是二维的时候所用到的是第i-1层的。
第二种理解方式是如果从大到小,一种草药就可能被采摘多次。这就变成了完全背包。
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
typedef pair<int, int> PII;
int t, m;
int dp[1010];
PII h[110];
int main()
{
cin >> t >> m;
for (int i = 1; i <= m; i++)
cin >> h[i].first >> h[i].second;
for (int i = 1; i <= m; i++) {
for (int j = t; j >= h[i].first; j--) {
dp[j] = max(dp[j], dp[j - h[i].first] + h[i].second);
}
}
cout << dp[t];
return 0;
}
附搜索代码和记忆化搜索代码
搜索代码:
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
typedef pair<int, int> PII;
int ans;
PII h[110];
int t, m;
void dfs(int level, int timeleft, int val) {
if (level == m + 1) {
ans = max(ans, val);
return;
}
if (timeleft >= h[level].first)
dfs(level + 1, timeleft - h[level].first, val + h[level].second);
dfs(level + 1, timeleft, val);
}
int main()
{
cin >> t >> m;
for (int i = 1; i <= m; i++)
cin >> h[i].first >> h[i].second;
dfs(1, t, 0);
cout << ans;
return 0;
}
记忆化搜索:
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
typedef pair<int, int> PII;
int ans;
PII h[110];
int t, m;
int mem[110][1010];
int dfs(int level, int timeleft) {
if (mem[level][timeleft] != -1)
return mem[level][timeleft];
if (level == m + 1) {
return mem[level][timeleft] = 0;
}
int dfs1 = -1, dfs2;
if (timeleft >= h[level].first)
dfs1 = dfs(level + 1, timeleft - h[level].first) + h[level].second;
dfs2 = dfs(level + 1, timeleft);
return mem[level][timeleft] = max(dfs1, dfs2);
}
int main()
{
memset(mem, -1, sizeof mem);
cin >> t >> m;
for (int i = 1; i <= m; i++)
cin >> h[i].first >> h[i].second;
cout << dfs(1, t);
return 0;
}