【题解】P1855 榨取kkksc03

某天刷洛谷的时候看到这道题,被标题骗进来了(标题好爽(雾

毋庸置疑这是一道大水题,大概是看在 kkk 的面子上才标了「普及/提高-」难度

那么决定了,第一篇题解就给它吧!(其实只是想练习一下 MathJax 的使用(

题目

P1855 榨取kkksc03

思路

题目的废话很多(其实是洛谷的广告吧),抛开那些废话你会发现这是一道背包问题

我们只需将每个愿望消耗的时间和金钱看作费用,而每个愿望的价值都看作 $1$,那么这就是一道二维费用的 $01$ 背包问题

于是乎咱开一个三维数组 $f[i][j][k]$,其数值表示前 $i$ 个愿望恰好消耗 $j$ 的时间和 $k$ 的金钱所产生的最大价值,由于我们已经将每个愿望的价值看作 $1$,产生的最大价值也就等价于最大的愿望数

我们再用 $a[i]$ 表示第 $i$ 个愿望所消耗的时间,用 $b[i]$ 表示第 $i$ 个愿望所消耗的金钱

根据题意,得出状态转移方程:

$$f[i][j][k]=max(f[i-1][j][k],f[i-1][j-a[i]][k-b[i]]+1)$$

稍微解释一下,对于第 $i$ 个愿望,有“实现”还是“不实现”两种策略

如果我们实现第 $i$ 个愿望,那么前 $i$ 个愿望恰好消耗 $j$ 的时间和 $k$ 的金钱所产生的最大价值就等于前 $i-1$ 个愿望恰好消耗 $j-a[i]$ 的时间和 $k-b[i]$ 的金钱所产生的最大价值再加上第 $i$ 个愿望的价值(也就是 $1$)

如果我们不实现第 $i$ 个愿望,那么前 $i$ 个愿望恰好消耗 $j$ 的时间和 $k$ 的金钱所产生的最大价值就等于前 $i-1$ 个愿望恰好消耗 $j$ 的时间和 $k$ 的金钱所产生的最大价值

对于两种策略再取二者中的最大值,就可以得到当前阶段的最优策略

再来看看边界条件,首先初始化三维数组中的每一项为 $0$,不多解释

其次状态转移方程里有 $j-a[i]$ 和 $k-b[i]$,有没有想过这两项有可能减出来是负数呢?

所以代码中除了基本的三层循环外,还需要加入判断,一旦两者中存在一者为负,那么我们直接让它继承 $f[i-1][j][k]$ 的数值,也就是:

$$f[i][j][k]=f[i-1][j][k]$$

(我才不会告诉你我这题 WA 了两次就是因为这个←_←)

代码

dp 的代码实现还是相对比较容易的,直接上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <bits/stdc++.h>

using namespace std;

int f[101][201][201];
int a[101];
int b[101];

int main() {
int n,m,t;
int ans=0;
cin>>n>>m>>t;
for (int i=1; i<=n; i++) {
cin>>a[i]>>b[i];
}
for (int i=1; i<=n; i++) {
for (int j=1; j<=m; j++) {
for (int k=1; k<=t; k++) {
if (j>=a[i] && k>=b[i]) {
f[i][j][k]=max(f[i-1][j][k],f[i-1][j-a[i]][k-b[i]]+1);
} else {
f[i][j][k]=f[i-1][j][k];
}
}
}
}
cout<<f[n][m][t]<<endl;
return 0;
}

原谅我代码写得这么丑ㄟ(▔ ,▔)ㄏ

反思

这个算法的时间复杂度是 $O(N*M*T)$,空间复杂度也是 $O(N*M*T)$,对于这道题的数据规模足矣,但是显然还有更加优化的做法

我们可以考虑降维成二维数组进一步优化空间复杂度,事实上 $i$ 的这一维是可以省去的,只需略微改动一下状态转移方程和循环结构,不断覆盖上一次的无用数据:

$$f[j][k]=max(f[j][k],f[j-a[i]][k-b[i]]+1)$$

这样我们就得到了一个空间复杂度为 $O(M*T)$ 的做法~

当然还有记忆化搜索等做法,各位可以自己去研究哈~

那么本篇题解就是这样,kkksc03 已经被我们榨取得一滴不剩(大雾)



评论

如果您看不见评论框,八成是因为您还未接入真正意义上自由开放的互联网哟~自己看着办吧~

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×