Cut the Sequence 【单调队列(栈)+堆优化DP】

题目链接 POJ - 3017


  超级好的一道题目,从早上先写一个诡异的线段树(当然是错的),逐步推到了单调栈和单调队列,历时很久很久——题记。

  将我的解题思路慢慢的展开:

  首先,为什么会想到线段树呢,因为这很容易化简成为一个区间dp的形式,就是

\large dp[i] = min(dp[j] + max(a_{k})) ,(pos\leq j< i, j < k \leq i)

  这个dp还是比较容易想到的,然后呢,看到这里有一个区间最小值,然后再来就是一个单点更新,以及一个不断放进去的\large a_k很容易想到用线段树来维护一下这个区间,但是呢,显然是错误的。

  列出错误的原因:因为我们不一定取的是区间任意的j这个点和这整个区间的最小的最大值的\large a_k进行合成,这样实际复杂度又会变得不正确了,所以接下去讲正解。

  正解:首先,我们需要明白一个前提,假设\large dp[i]是以第i位为结尾的答案值,那么,会发现\large dp[i]是单调不减的,这是很重要的一条信息,根据它,我们可以有如下的推断:

  如果说,我们的a[ ]数据是升序的,那么肯定是取最早出现的是最优解,譬如说现在\large i < j\large dp[i] \leq dp[j]当且仅当\large a[i] > a[j]二者才有可比性,不然选择\large dp[i]一定是更优的(此时\large a[i] \leq a[j])。

  所以,我们只要处理出来所有需要比较的点集就可以了,没有必要比较的,我们就把他们删除好了,什么是具有比较效应的呢,也就是我们的a[ ]数组就一定要降序才可能存在,可以取出一些单调递减的a[ ]子序列,满足单调递减,这样才会满足可比较性,然后将它们的值放入一个堆内存最小值就可以了,这里我用的是multiset,因为可能存在重复的值,而且还满足删除和插入的操作,比较灵活。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <bitset>
//#include <unordered_map>
//#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
#define MP(a, b) make_pair(a, b)
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 1e5 + 7;
int N, top, fail;
ll M, pre[maxN], a[maxN], dp[maxN];
multiset<ll> st;
multiset<ll>::iterator it;
struct node
{
    int id; ll aval;
    node(int a=0, ll v=0):id(a), aval(v) {}
} que[maxN];
int main()
{
    scanf("%d%lld", &N, &M);
    bool ok = true;
    for(int i=1; i<=N; i++)
    {
        scanf("%lld", &a[i]); if(a[i] > M) ok = false;
        pre[i] = pre[i - 1] + a[i];
        dp[i] = INF;
    }
    if(!ok) { printf("-1\n"); return 0; }
    dp[0] = 0;
    top = fail = 0;
    for(int i=1; i<=N; i++)
    {
        while(top < fail && pre[i] - pre[que[top].id - 1] > M)
        {
            it = st.find(dp[que[top].id - 1] + que[top].aval);
            st.erase(it);
            que[top].id++;
            if(top + 1 < fail)
            {
                if(que[top].id >= que[top + 1].id)
                {
                    top++;
                }
                else
                {
                    st.insert(dp[que[top].id - 1] + que[top].aval);
                }
            }
            else if(que[top].id >= i)
            {
                top++;
            }
            else
            {
                st.insert(dp[que[top].id - 1] + que[top].aval);
            }
        }
        int old_id = -1;
        while(top < fail && que[fail - 1].aval < a[i])
        {
            old_id = que[fail - 1].id;
            it = st.find(dp[que[fail - 1].id - 1] + que[fail - 1].aval);
            st.erase(it);
            fail--;
        }
        if(top == fail || que[fail - 1].aval > a[i])
        {
            que[fail++] = node(~old_id ? old_id : i, a[i]);
            st.insert(dp[que[fail - 1].id - 1] + a[i]);
        }
        dp[i] = *st.begin();
    }
    printf("%lld\n", dp[N]);
    return 0;
}
/*
7 7
0 7 0 0 1 1 2
ans:9
*/
发布了852 篇原创文章 · 获赞 1016 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/qq_41730082/article/details/104662359
cut