SCOI2014 方伯伯的玉米田(动态规划+树状数组优化)

题目

Description

方伯伯在自己的农田边散步,他突然发现田里的一排玉米非常的不美。

这排玉米一共有N株,它们的高度参差不齐。
方伯伯认为单调不下降序列很美,所以他决定先把一些玉米拔高,再把破坏美感的玉米拔除掉,使得剩下的玉米的高度构成一个单调不下降序列。

方伯伯可以选择一个区间,把这个区间的玉米全部拔高1单位高度,他可以进行最多K次这样的操作。拔玉米则可以随意选择一个集合的玉米拔掉。

问能最多剩多少株玉米,来构成一排美丽的玉米。

Input

第1行包含2个整数nK,分别表示这排玉米的数目以及最多可进行多少次操作。

第2行包含n个整数,第i个数表示这排玉米,从左到右第i株玉米的高度ai

Output

输出1个整数,最多剩下的玉米数。

Sample Input
3 1
2 1 3
Sample Output
3
HINT

1 < N < 100001 < K ≤ 5001 ≤ ai ≤5000

题解

我最恨的就是DP了!!!

不过为了讲课还是做了一下,毕竟tkys_Austin大佬都推荐了。

首先,通过思考我们可以得出每次操作区间的右端点一定为n,否则后面赫鲁晓夫玉米的高度相对前面的高度就会减少,被拔的也就会变多,不满足最优

我们可以以DP[i][j]表示被操作j次后以i为右端点的最长不下降子序列长度,显然,i被操作了j次,高度就为Orig[i]+j

根据这些我们就能得出状态转移方程:

dp[i][j]=max{dp[k][p]+1}, a[k]+p≤a[i]+j, p≤j, k<i

就可以得到代码了:

#include<cstdio>
#define MAX(a, b) a > b ? a : b
using namespace std;
int f(-1);
inline char GetCharacter() {
    static char buf[2000000], *p1 = buf, *p2 = buf;
    return (p1 == p2) &&
           (p2 = (p1 = buf) + fread(buf, 1, 2000000, stdin), p1 == p2) ? 
           EOF : *p1++;
}
#define IS_DIGIT(c) (c >= '0' && c <= '9')
inline void Read(int &x) {
    f = 1, x = 0;
    static char c = GetCharacter();
    while (!IS_DIGIT(c)) {
        if (c == '-') f = -1;
        c = GetCharacter();
    }
    while (IS_DIGIT(c)) x = x * 10 + c - '0', c = GetCharacter();
    x *= f;
}
#undef IS_DIGIT
int n,K;
int origin[10010];
int dp[10010][510];
int main(int argc, char **argv) {
    Read(n), Read(K);
    for (register int i(1); i <= n; ++i) Read(origin[i]);
    for (register int i(1); i <= n; ++i) {
        for (register int j(K); j >= 0; --j) {
            for (register int x(0); x < i; ++x) {
                for (register int y(K); y >= 0; --y) {
                    if (origin[x] + y <= origin[i] + j) {
                        dp[i][j] = MAX(dp[i][j], dp[x][y] + 1);
                    }
                }
            }
        }
    }
    printf("%d\n", dp[n][K]);
    return 0;
}

亲身体验之后完美爆零

因为这样的时间复杂度是 \(\Theta(n^{2}k^{2})\) ,直接爆炸

怎么办呢?我们可以用二维树状数组优化

二维树状数组写法差不多,只是多了一重循环。基本思想不变,时间复杂度被优化到了\(\Theta(nklgnlgk)\) ,就AC了

代码 code
#include<cstdio>
#define MAX(a, b) a > b ? a : b
using namespace std;
int f(-1);
inline char GetCharacter() {
    static char buf[2000000], *p1 = buf, *p2 = buf;
    return (p1 == p2) &&
           (p2 = (p1 = buf) + fread(buf, 1, 2000000, stdin), p1 == p2) ? 
           EOF : *p1++;
}
#define IS_DIGIT(c) (c >= '0' && c <= '9')
inline void Read(int &x) {
    f = 1, x = 0;
    static char c = GetCharacter();
    while (!IS_DIGIT(c)) {
        if (c == '-') f = -1;
        c = GetCharacter();
    }
    while (IS_DIGIT(c)) x = x * 10 + c - '0', c = GetCharacter();
    x *= f;
}
#undef IS_DIGIT
int origin[10010], fenwick_tree[10010][510];
int n, K, max_original_height, sum, ans;
#define LOWBIT(i) ((i)&(-i))
inline void Update(register int index1, register int index2, const int &delta) {
    register int index(index2);
    while (index1 <= max_original_height + K) {
        index2 = index;
        while (index2 <= K + 1) {
            fenwick_tree[index1][index2] = MAX(fenwick_tree[index1][index2], 
                                               delta);
            index2 += LOWBIT(index2);
        }
        index1 += LOWBIT(index1);
    }
}
inline int GetSum(register int index1, register int index2){
    register int ret(0), index(index2);
    while (index1) {
        index2 = index;
        while (index2){
            ret = MAX(ret, fenwick_tree[index1][index2]);
            index2 -= LOWBIT(index2);
        }
        index1 -= LOWBIT(index1); 
    }
    return ret;
}
#undef LOWBIT
int main(int argc, char **argv){
    Read(n), Read(K);
    for (register int i(1); i <= n; ++i) Read(origin[i]), 
        max_original_height = MAX(max_original_height, origin[i]);
    for (register int i(1); i <= n; ++i) {
        for (register int j(K); j >= 0; --j) {
            sum = GetSum(origin[i] + j, j + 1) + 1;
            Update(origin[i] + j, j + 1, sum);
            ans = MAX(ans, sum);
        }
    } 
    printf("%d\n", ans);
    return 0;
}

转载于:https://www.cnblogs.com/forth/p/9318506.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值