zoukankan      html  css  js  c++  java
  • 【学习笔记】动态dp

    概述

    动态$dp$是一类需要对$dp$的输入数据进行修改,并在修改后要快速查询的问题。

    求解动态$dp$最基本的思路是分治。

    一直在想动态$dp$是不是叫动态动态规划(误

    例题

    SPOJ 1716

    从题目开始说起吧,这样会比较好说。

    0

    可以比较直观地看出线段树的做法。

    每个节点,维护一个区间和,前缀最大和,后缀最大和,和最大子段和。

    然后就可以单点修改,区间查询。

    #include<cstdio>
    #include<algorithm>
    #include<vector>
    #include<cstring>
    #include<queue>
    #include<cstdlib>
    using namespace std;
    #define N 500005
    #define ll long long
    #define INF 0x3f3f3f3f
    struct node{
        int sum,ms/*maxsum*/,ml,mr/*maxl,maxr*/;
    }tree[N*4];
    int n,a[N];
    inline int rd()
    {
        int f=1,x=0;char c=getchar();
        while(c<'0'||'9'<c){if(c=='-')f=-1;c=getchar();}
        while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
        return f*x;
    }
    void PushUp(int i)
    {
        tree[i].sum=tree[i<<1].sum+tree[i<<1|1].sum;
        tree[i].ml=max(tree[i<<1].sum+tree[i<<1|1].ml,tree[i<<1].ml);
        tree[i].mr=max(tree[i<<1|1].sum+tree[i<<1].mr,tree[i<<1|1].mr);
        tree[i].ms=max(max(tree[i<<1].ms,tree[i<<1|1].ms),tree[i<<1].mr+tree[i<<1|1].ml);
    }
    void build(int i,int l,int r)
    {
        if(l==r)
        {
            tree[i].sum=tree[i].ml=tree[i].mr=tree[i].ms=a[l];
            return ;
        }
        int mid=(l+r)>>1;
        build(i<<1,l,mid);
        build(i<<1|1,mid+1,r);
        PushUp(i);
    }
    void update(int i,int pos,int val,int l,int r)
    {
        if(l==r)
        {
            tree[i].ms=tree[i].ml=tree[i].mr=tree[i].sum=val;
            return ;
        }
        int mid=(l+r)>>1;
        if(pos<=mid)
            update(i<<1,pos,val,l,mid);
        else update(i<<1|1,pos,val,mid+1,r);
        PushUp(i);
    }
    node query(int i,int li,int ri,int l,int r)
    {
        if(l<=li&&ri<=r)
            return tree[i];
        int mid=(li+ri)>>1;
        if(r<=mid) return query(i<<1,li,mid,l,r);
        else if(l>mid) return query(i<<1|1,mid+1,ri,l,r);
        else
        {
            node x=query(i<<1,li,mid,l,r),y=query(i<<1|1,mid+1,ri,l,r),res;
            res.sum=x.sum+y.sum;
            res.ml=max(x.sum+y.ml,x.ml);
            res.mr=max(y.sum+x.mr,y.mr);
            res.ms=max(max(x.ms,y.ms),x.mr+y.ml);
            return res;
        }
    }
    int main()
    {
        n=rd(); 
        for(int i=1;i<=n;i++)
            a[i]=rd();
        build(1,1,n);
        int Q=rd();
        while(Q--)
        {
            int opt=rd();
            if(opt==1)
            {
                int x=rd(),y=rd(),tmp;
                if(x>y) tmp=x,x=y,y=tmp;
                printf("%d
    ",query(1,1,n,x,y).ms);
            }
            else if(opt==0)
            {
                int x=rd(),y=rd();
                update(1,x,y,1,n); 
            }
        }
        return 0;
    }
    View Code

    1

    那么动态$dp$是怎么做的呢

    先不考虑修改,我们来想想一个正常的$dp$怎么来求全局最大子段和。

    定义状态$f[i]$表示以$i$结尾的最大子段和,$g[i]$表示$1~i$的最大子段和

    转移如下:
    $f[i]=max(f[i-1]+a[i],a[i])$

    $g[i]=max(g[i-1],f[i-1]+a[i],a[i])$

    重定义矩阵乘法:把$*$重定义为$+$,$+$重定义为取$max$

    即:

     把式子写成矩阵的形式(设转移矩阵为$C$

    可以算出转移矩阵:

    然后求$[l,r]$的值就是一个区间矩阵乘法,可以用线段树维护(还是要用线段树

    #include<cstdio>
    #include<algorithm>
    #include<queue>
    #include<cstring>
    #include<iostream>
    #include<cstdio>
    using namespace std;
    #define N 500005
    #define INF 0x3f3f3f3f
    #define LL long long
    int rd()
    {
        int x=0,f=1;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
        while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48); c=getchar();}
        return f*x;
    }
    int n,a[N];
    struct Matrix{
        int m[4][4];
        Matrix(){ memset(m,0,sizeof(m)); }
        int* operator [](int i){ return m[i]; }
    };
    Matrix operator * (Matrix a,Matrix b)
    {
        Matrix c;
        for(int i=1;i<=3;i++)
            for(int j=1;j<=3;j++)
                c[i][j]=-INF;
        for(int i=1;i<=3;i++)
            for(int k=1;k<=3;k++)
                for(int j=1;j<=3;j++)
                    c[i][j]=max(c[i][j],a[i][k]+b[k][j]);
        return c;
    }
    Matrix tree[N<<2];
    void PushUp(int i)
    {
        tree[i]=tree[i<<1]*tree[i<<1|1];
    }
    void Build(int i,int l,int r)
    {
        if(l==r)
        {
            tree[i][1][1]=tree[i][1][3]=tree[i][2][1]=tree[i][2][3]=a[l];
            tree[i][1][2]=tree[i][3][1]=tree[i][3][2]=-INF;
            tree[i][2][2]=tree[i][3][3]=0;
            return ;
        }
        int mid=(l+r)>>1;
        Build(i<<1,l,mid);
        Build(i<<1|1,mid+1,r);
        PushUp(i);
    }
    void Modify(int i,int l,int r,int pos,int val)
    {
        if(l==r)
        {
            tree[i][1][1]=tree[i][1][3]=tree[i][2][1]=tree[i][2][3]=val;
            return ;
        }
        int mid=(l+r)>>1;
        if(pos<=mid) Modify(i<<1,l,mid,pos,val);
        else Modify(i<<1|1,mid+1,r,pos,val);
        PushUp(i);
    }
    Matrix Query(int i,int l,int r,int ql,int qr)
    {
        if(ql<=l&&r<=qr) return tree[i];
        int mid=(l+r)>>1;
        if(qr<=mid) return Query(i<<1,l,mid,ql,qr);
        else if(ql>mid) return Query(i<<1|1,mid+1,r,ql,qr);
        return Query(i<<1,l,mid,ql,qr)*Query(i<<1|1,mid+1,r,ql,qr);
    }
    int main()
    {
        n=rd();
        for(int i=1;i<=n;i++)
            a[i]=rd();
        Build(1,1,n);
        int Q=rd();
        while(Q--)
        {
            int opt=rd(),x=rd(),y=rd();
            if(opt==0)
            {
                a[x]=y;
                Modify(1,1,n,x,y);
            }
            else
            {
                Matrix ans=Query(1,1,n,x,y);
                printf("%d
    ",max(ans[2][1],ans[2][3]));//
            }
        }
        return 0;
    }
    View Code
  • 相关阅读:
    出现System.web.mvc冲突的原因及解决方法CS0433
    看完此文还不懂NB-IoT,你就过来掐死我吧...
    html5调用手机陀螺仪实现方向辨识
    黑盒测试和白盒测试的区别
    CentOS7 下 keepalived 的安装和配置
    centos 下 mysql+keepalived实现双主自由切换
    MySQL 高可用性—keepalived+mysql双主(有详细步骤和全部配置项解释)
    备份VMware虚拟磁盘文件 移植到其他虚拟机
    Centos7 Mysql 双机热备实现数据库高可用
    CentOS7配置Mysql热备份
  • 原文地址:https://www.cnblogs.com/lyttt/p/13395294.html
Copyright © 2011-2022 走看看