P1048 采药(洛谷)
中文题面,不描述。
这道题记忆化搜索和dp都可以实现,实际上就是一个0-1背包问题。选和不选获得一个最大值输出即可。
这里给出两种解决方案。
记忆化搜索:
dp[i][j]表示第i件物品下,时间耗费为j的最大价值。
dp[][]初始话为-1
因为在我们dfs内部如果当前i大于了m的话,直接返回的是0,是无效的。(也会算作已经处理的情况)
当总的时间j大于等于采集第i件物品的时间时,那么对于第i件物品,有选和不选两种选择,维护最大的即可。
当总的时间j大于采集第i件物品的时间时,只能放弃第i件物品。继续搜下去。
最后输出即可。
dp:
这个是0-1背包
dp[i]表示时间耗费为i时的最大价值。
我们就可以二重循环遍历即可。
对于第i件物品,从总的时间t开始到tim[i]结束。维护dp[j]的一个最大值。从前面的dp[]转移过来维护一个最大值即可。
记得初始话dp[]为0
对应的还有一道题是疯狂的采药,是一道多重背包问题~
传送门
跟0-1背包不同的是多重背包可以无限次数的获取,所以多重背包从前往后,0-1背包从后往前;
这道题对应的关键部分是:
for (int i = 1; i <= m; i++)
{
for (int j = tim[i]; j <= t; j++)
{
dp[j] = max(dp[j], dp[j - tim[i]] + value[i]);
}
}
记忆化搜索的代码部分:
#include <bits/stdc++.h>
#define mst(a, n) memset(a, n, sizeof(a))
using namespace std;
const int N = 1e3 + 10;
int tim[N];
int value[N];
int t, m;
int dp[N][N];
int dfs(int x, int y)
{
if (x > m)
{
return 0;
}
if (dp[x][y] != -1)
{
return dp[x][y];
}
if (y >= tim[x])
{
return dp[x][y] = max(dfs(x + 1, y), dfs(x + 1, y - tim[x]) + value[x]);
}
else
{
return dp[x][y] = dfs(x + 1, y);
}
}
int main()
{
mst(dp, -1);
cin >> t >> m;
for (int i = 1; i <= m; i++)
{
cin >> tim[i] >> value[i];
}
cout << dfs(1, t) << endl;
return 0;
}
dp代码部分:
#include <bits/stdc++.h>
#define mst(a, n) memset(a, n, sizeof(a))
using namespace std;
const int N = 1e3 + 10;
int tim[N];
int value[N];
int t, m;
int dp[N];
int main()
{
cin >> t >> m;
for (int i = 1; i <= m; i++)
{
cin >> tim[i] >> value[i];
}
for (int i = 1; i <= m; i++)
{
for (int j = t; j >= tim[i]; j--)
{
dp[j] = max(dp[j], dp[j - tim[i]] + value[i]);
}
}
cout << dp[t] << endl;
return 0;
}