zoukankan      html  css  js  c++  java
  • POJ 3017 Cut the Sequence(DP + 单调队列优化 + 平衡树)

    题意:

    长度为 n 的数列,要求把这个数列划分为任意块,每块的元素和小于 m,使得所有块的最大值的和最小。

    思路:

    1. 很明显的一个转移方程是: dp[i] = max(dp[j] + max(a[j+1], a[j+2], ..., a[i])); 其中满足 sum[i] - sum[j] <= M 的 j 都算,这是一个 O(N * N) 的解法。

    2. 如果不去优化枚举 j 的这个过程,就会直接 TLE,如何优化?联系到求最大值,可以想到用最大值的单调队列,队列里面存放的是以 i 为结尾,窗口里面的元素和

       恰好不大于 M 的 j 为左边界,即恰好有 sum[i] - sum[j-1] <= M.

    3. 如果是这样的话,还不能够达到目标,因为 1 中最优值点的 j 的选择可能是窗口中的任意一个值,这时候遇到一个困难:确定 j 的位置,使情况最优。

    4. 考虑到 dp[i] 是一个单调增的数组,而窗口中的元素是关于最大值递减的,先让 M 足够大,从简单的角度来考虑:对于 a[1] = 8, a[2] = 5, a[3] = 7,对于 dp[3]

       则有 dp[3] = min(dp[0] + 8, dp[1] + 7, dp[2] + 7),显然 dp[1] <= dp[2],则进一步优化有 dp[3] = min(dp[0] + 8, dp[1] + 7);

    5. 从 4 中我们可以看出点端倪,单调队列中所指向的元素也恰好是 8, 7,所以得出的结论有:对于 dp[i] 只要选择单调队列中所指向的位置的 j 即可保证结果是最优的。

    6. 如何维护单调队列中的 dp[j] + max(a[j+1], a[j+2], ..., a[i]),则考虑使用一个平衡树,这样可以把时间复杂度降到最低。

    #include <iostream>
    #include <set>
    #include <algorithm>
    using namespace std;
    
    #define LL long long int
    
    const int MAXN = 100010;
    
    int num[MAXN], deq[MAXN];
    LL dp[MAXN];
    multiset<LL> optset;
    
    int main()
    {
        LL N, M;
    
        scanf("%lld %lld", &N, &M);
        for (int i = 1; i <= N; ++i)
            scanf("%d", &num[i]);
    
        LL sum = 0;
        int s = 0, e = -1, p = 1;
        bool flag = false;
    
        optset.clear();
        for (int i = 1; i <= N; ++i)
        {
            if (num[i] > M)
            {
                flag = true;
                break;
            }
    
            sum += num[i];
            while (sum > M)
                sum -= num[p++];
    
            while (s <= e && num[i] >= num[deq[e]])
            {
                if (s < e)
                    optset.erase(dp[deq[e-1]] + num[deq[e]]);
                --e;
            }
    
            deq[++e] = i;
            if (s < e)
                optset.insert(dp[deq[e-1]] + num[deq[e]]);
    
            while (deq[s] < p)
            {
                if (s < e)
                    optset.erase(dp[deq[s]] + num[deq[s+1]]);
                ++s;
            }
    
            dp[i] = dp[p-1] + num[deq[s]];
            if (s < e && dp[i] > *optset.begin())
                dp[i] = *optset.begin();
        }
        if (flag)
            dp[N] = -1;
        printf("%lld\n", dp[N]);
        return 0;
    }
  • 相关阅读:
    Codevs 2296 仪仗队 2008年省队选拔赛山东
    Codevs 1535 封锁阳光大学
    Codevs 1069 关押罪犯 2010年NOIP全国联赛提高组
    Codevs 1218 疫情控制 2012年NOIP全国联赛提高组
    Codevs 1684 垃圾陷阱
    洛谷 P1108 低价购买
    Vijos P1325桐桐的糖果计划
    Codevs 3289 花匠 2013年NOIP全国联赛提高组
    Codevs 2611 观光旅游(floyed最小环)
    C语言基础之彩色版C语言(内含linux)
  • 原文地址:https://www.cnblogs.com/kedebug/p/2939428.html
Copyright © 2011-2022 走看看