zoukankan      html  css  js  c++  java
  • POJ3016-K-Monotonic(左偏树+DP)

    我觉得我要改一下签名了……怎么会有窝这么啰嗦的人呢?

    做这题需要先学习左偏树左偏树的特点及其应用》 然后做一下POJ3666,这题的简单版。

    思路:

    考虑一下维护中位数的过程
    原数组为A,找到的不降数列为B
    当对于A的前n个数已经找好了最优解B[1…n],可知此时A被分成很多块,并被一些大顶堆记录,假设第i块有num个数,那么第i个堆维护这一块的最小的(num+1)/2个数,堆顶即为中位数。
    假设已经处理好前7个数,被分为两块 ([a,b],c,d) ([h,e],f) (每一块按升序排列,[]中的数是堆里面维护的。
    因为数列是不降的,所以b≤e
    当新添加一个元素的时候,设为x,如果x≤e,将需要向前合并。
    那么新的块应该是……分两种情况……
    1.x>h ([h,x],e,f)
    2.x<h ([x,h],e,f)

    设新的中位数是val=max(h, x)  分类讨论一下可以发现改变的值是(e-val)+(val-x)


    这里假设是([x,h],e,f)
    当h<b时,需要继续向前合并。
    合并之后是([x,h,a,b],c,d,e,f) (顺序已经不确定了,只能确定栈中元素和栈顶是b

    可以发现大小为偶数的块和偶数的块合并,合并后的堆不需要弹出元素。
    合并前([a,b],c,d) 的中位数是b ([x,h],e,f)的中位数是h, 合并后的中位数的b
    可知答案改变都是发生在集合([x,h],e,f)中的,我们又知道b≤e(上面提到过),那么很容易得到答案是不变哒!(就是把(h-x)+(h-h)+(e-h)+(f-h)变成了(b-x)+(b-h)+(e-b)+(f-b),值是一样的

    上面是偶数和偶数合并,继续讨论前一块奇数和后一块偶数合并。
    设前一块是([a,b],c) 中位数是b,后一块是([d,e],f,g)中位数是e,合并后不需要弹出,中位数是b,类似上面的情况,我们可以得出b≤f,所以答案仍然不变。

    前一块偶数,后一块奇数
    ([a,b],c,d)中位数是b ([e,f],g)中位数是f 合并后不需要弹出 中位数是b 其中(f<b≤g)
    那么答案由([e,f],g)的改变产生,f的左右两边是可以抵消掉的,改变只会因为f,改变的值是b-f

    前一块奇数,后一块奇数
    设前一块是([a,b],c) 中位数是b,后一块是([d,e],f,)中位数是e 其中e<b≤f
    合并后弹出元素b,中位数为max(a,e),设为val
    那么答案改变就是b-e


    到此,所有情况都讨论完了ˊ_>ˋ

    结论:当一个块和前面的块合并时,如果当前块的数量为偶数,答案不变,否则答案增加(前一块的中位数-当前块的中位数)

    代码:

    /*****************************************
    Problem: 3016        User: G_lory
    Memory: 12676K        Time: 1797MS
    Language: G++        Result: Accepted
    *****************************************/
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int N = 2005;
    const int INF = 0x5f5f5f5f;
    typedef long long ll;
    
    struct LTree {
        int l, r, sz;
        int key, dis;
        bool operator<(const LTree lt) const {
            return key < lt.key;
        }
    } tr[N];
    int cnt_tr;
    
    int NewTree(int k) {
        tr[++cnt_tr].key = k;
        tr[cnt_tr].l = tr[cnt_tr].r = tr[cnt_tr].dis = 0;
        tr[cnt_tr].sz = 1;
        return cnt_tr;
    }
    
    int Merge(int x, int y) {
        if (!x || !y) return x + y;
        if (tr[x] < tr[y]) swap(x, y);
        tr[x].r = Merge(tr[x].r, y);
        if (tr[tr[x].l].dis < tr[tr[x].r].dis) swap(tr[x].l, tr[x].r);
        tr[x].dis = tr[tr[x].r].dis + 1;
        tr[x].sz = tr[tr[x].l].sz + tr[tr[x].r].sz + 1;
        return x;
    }
    
    int Top(int x) {
        return tr[x].key;
    }
    
    void Pop(int &x) {
        x = Merge(tr[x].l, tr[x].r);
    }
    
    int root[N], num[N];
    void cal(int a[], int n, int ans[]) {
        int res;
        cnt_tr = res = 0;
        int cnt = 0;
        for (int i = 0; i < n; ++i) {
            root[++cnt] = NewTree(a[i]);
            num[cnt] = 1;
            while (cnt > 1 && Top(root[cnt]) < Top(root[cnt-1])) {
                cnt--;
                if (num[cnt+1]&1) res += Top(root[cnt]) - Top(root[cnt+1]);
                root[cnt] = Merge(root[cnt], root[cnt+1]);
                num[cnt] += num[cnt+1];
                while (tr[root[cnt]].sz*2 > num[cnt]+1) {
                    Pop(root[cnt]);
                }
                int now = Top(root[cnt]);
                
            }
            ans[i] = res;
        }
    }
    
    int a[N], b[N], c[N];
    int in[N][N], de[N][N];
    int dp[N][N];
    int main() {
        //freopen("in", "r", stdin);
        int n, k;
        while (~scanf("%d%d",&n, &k) && n) {
            for (int i = 1; i <= n; ++i) {
                scanf("%d", a+i);
                b[i] = a[i]-i;
                c[i] = -a[i]-i;
            }
            for (int i = 1; i <= n; ++i) {
                cal(c+i, n-i+1, de[i]+i);
                cal(b+i, n-i+1, in[i]+i);
            }
            for (int i = 1; i <= n; ++i) dp[0][i] = INF;
            for (int i = 1; i <= k; ++i) {
                dp[i][0] = 0;
                for (int j = 1; j <= n; ++j) {
                    dp[i][j] = INF;
                    for (int p = 0; p < j; ++p) {
                        dp[i][j] = min(dp[i][j], dp[i-1][p] + min(in[p+1][j], de[p+1][j]));
                    }
                }
            }
            printf("%d
    ", dp[k][n]);
        }
        return 0;
    }

      

  • 相关阅读:
    中序遍历【递归算法】和【非递归算法】
    等价无穷小替换
    轮转访问MAC协议
    曲率
    Java I/O流 01
    Java 集合框架 04
    Java 集合框架 03
    Java 集合框架 02
    Java 集合框架 01
    Java 常见对象 05
  • 原文地址:https://www.cnblogs.com/wenruo/p/5798801.html
Copyright © 2011-2022 走看看