luogu P1107 雷涛的小猫 题解

本文介绍了一种针对柿子摘取问题的动态规划算法及其优化方法。通过以高度为阶段进行DP,并优化状态转移过程,避免了高时间复杂度的问题。文章详细解释了如何在更新过程中记录最大值并用于后续更新,最终实现高效求解。
摘要由CSDN通过智能技术生成

\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[1n],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) dpidelta,k(k[1n],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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值