\quad 不难发现,这是一道 d p dp dp题。看完题后,很容易能想到以高度为阶段来进行 d p dp dp。那么,状态设置也可以顺水推舟地想到: d p i , j dp_{i,j} dpi,j表示在第 j j j棵树,高度为 i i i的地方可获得的最大柿子数。
\quad 然后,我们就可以考虑状态转移。我们可知:得到 d p i , j dp_{i,j} dpi,j的方式有两种,一是从 d p i + 1 , j dp_{i + 1,j} dpi+1,j转移来,二是从 m a x d p i + d e l t a , k ( k ∈ [ 1 ∼ n ] , k ≠ j ) max_{dp_{i+delta,k}(k\in[1\sim n],k\not =j)} maxdpi+delta,k(k∈[1∼n],k=j)转移来。如果硬按照这个转移式来写,不难发现,时间复杂度是 O ( n 2 h ) O(n^2h) O(n2h),显然会 T L E TLE TLE。那么我们就可以将取 m a x max max的步骤进行优化。但是本题解的优化方法与其他的可能有些许区别。
\quad
想优化,最简单的就是另开一个数组记录高度为
i
i
i时
d
p
i
,
j
dp_{i,j}
dpi,j的最大值,在转移时直接调用。这一优化,本质还在于用前面已更新的来更新自己。然而,本题解的优化方法,本质是用已更新的自己来更新后面。也就是,在对
d
p
i
,
j
dp_{i,j}
dpi,j进行转移时,顺便记录下最大值,然后用它去更新
d
p
i
−
d
e
l
t
a
,
k
(
k
∈
[
1
∼
n
]
,
k
≠
j
)
dp_{i-delta,k}(k \in [1 \sim n],k\not = j)
dpi−delta,k(k∈[1∼n],k=j)。其实并没有太大区别……
\quad 具体细节见 C o d e : Code: Code:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2010;
int n, h, d;
int dp[maxn][maxn];
int a[maxn][maxn];
inline int read() {
int x = 0, f = 1;
char ch = getchar();
while(!isdigit(ch)) {
if(ch == '-') f = -1;
ch = getchar();
}
while(isdigit(ch)) {
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int main() {
n = read(), h = read(), d = read();
for(int i = 1; i <= n; i ++) {
int x = read();
for(int j = 1; j <= x; j ++)
a[i][read()] ++;
}
for(int i = 1; i <= n; i ++)
for(int j = 0; j <= h; j ++)
dp[i][j] = a[i][j];//直接将a数组全部赋给dp数组
for(int i = h; i >= 0; i --) {
int maxx = -1, maxid;
for(int j = 1; j <= n; j ++) {
if(dp[j][i] > a[j][i]) {//是从别的树上跳过来的
dp[j][i] = max(dp[j][i], dp[j][i + 1] + a[j][i]);//判断是从别的树上跳过来收益大,还是从这棵树的上一个高度跳过来收益大
if(dp[j][i] > maxx) {//取最大值
maxx = dp[j][i];
maxid = j;
}
continue;
}
dp[j][i] += dp[j][i + 1];//从上一个高度跳下来
if(dp[j][i] > maxx) {//取最大值
maxx = dp[j][i];
maxid = j;
}
}
if(i >= d) {//用当前最大值去更新后面的
for(int j = 1; j <= n; j ++) {
if(j != maxid) dp[j][i - d] += maxx;
}
}
}
int maxx = -1;
for(int i = 1; i <= n; i ++) maxx = max(maxx, dp[i][0]);
printf("%d", maxx);
return 0;
}