zoukankan      html  css  js  c++  java
  • 复习动规(2)

    今天早上换寝室,耽误了一些时间。还是继续复习动态规划。

    单调队列优化DP

    第一道,宝物筛选一道多重背包优化题。如果用二进制优化很好做,但时间复杂度是O(nW*logm)。单调队列优化做法如下:

    首先做出普通的多重背包的转移方程:f[j]=max{f[j-w*k]+v*k},w为重量,v为价值。

    使得j=aw+b,即a=j/w,b=j%w。原方程可转化为f[j]=max{f[(a-k)*w+b]-(a-k)*v}+a*v。

    这个时候我们就可以枚举b,将a-k看做整体,对于每个b都对f[(a-k)*w+b]-(a-k)*v做单调队列,再将a从大到小循环就可以了。

    方程的转化比较难想到,代码的具体实现如下:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=4e4+1e3;
    int n,W,p[N];
    long long f[N],q[N];
    long long Max(long long x,long long y){
        return x>y?x:y;
    }
    int Min(int x,int y){
        return x<y?x:y;
    }
    int rd(){
        int s=0,ff=1;
        char w=getchar();
        while(!isdigit(w))
            ff^=w=='-',w=getchar();
        while(isdigit(w))
            s=s*10+w-'0',w=getchar();
        return ff?s:-s;
    }
    int main(){
        int v,w,m,l,r,a; long long x;
        n=rd(); W=rd();
        for(int i=1;i<=n;i++){
            v=rd(),w=rd(),m=rd();
            for(int b=0;b<w;b++){
                l=1,r=0,a=(W-(W%w-b+w)%w)/w;
                for(int k=1;k<=Min(a,m);k++){
                    x=f[(a-k)*w+b]-(a-k)*v;
                    while(l<=r&&q[r]<=x) r--;
                    q[++r]=x,p[r]=a-k;
                }
                for(int j=W-(W%w-b+w)%w;j!=b;j-=w){
                    a=j/w; while(p[l]>=a) l++;
                    f[j]=Max(f[j],q[l]+a*v);
                    if(a>=m+1) x=f[(a-m-1)*w+b]-(a-m-1)*v;
                    while(l<=r&&q[r]<=x) r--;
                    q[++r]=x,p[r]=a-m-1;
                }
            }
        }
        printf("%lld
    ",f[W]);
        return 0;
    }
    宝物筛选

    数据结构优化DP

    第一道,"动态 DP"&动态树分治剩下的大多数时间都花在上面了,头疼。

    动态DP具体的思想和做法看这篇博客吧,个人觉得写的非常好。

    我在这里讲下操作的具体实现(为了方便表达,把新定义的符号叫做乘号):

    对于点x,它的权值要改为y,首先它这个点的矩阵要先修改(别忘了权值要改)。(注意这个点的矩阵和线段树上的矩阵是两个矩阵

    然后进入while循环,先求出Top[x](Top[x]为x所在重链的顶点)的f值,求法是对x这一整条重链即线段树上的连续一段求矩阵的连乘,因为叶子节点的f值等于g值,所以乘出来后矩阵第1行第1列就是f[Top[x]][0],第2行第1列就是f[Top[x]][1]。

    再用x这个点的矩阵更新线段树上x的矩阵,之后再求出Top[x]的f值。

    再就可以用Top[x]的新旧f值修改树上“Top[x]的父亲”这个点的矩阵(具体实现看代码,好理解的)。然后x=“Top[x]的父亲”。

    然后while循环结束了,不用担心Top[x]的父亲也就是现在的x没有在线段树上更新,因为在下个循环会更新到的。

    答案就是求出1的f值,取最大值输出即可。代码具体实现如下:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e5+1e4;
    int n,m,cnt,tot,h[N],d[N],f[N][2],g[N][2],fa[N],dep[N],siz[N],Son[N],Top[N],dfn[N],sfn[N],End[N];
    int Max(int x,int y){
        return x>y?x:y;
    }
    struct nod{
        int nxt,to;
    }a[N*2];
    struct Num{
        int p[3][3];
        Num(){
            memset(p,0,sizeof(p));
            p[2][2]=-1e8;
        }
        inline Num operator*(Num b){
            Num c;
            for(int i=1;i<=2;i++)
                for(int j=1;j<=2;j++)
                    for(int k=1;k<=2;k++)
                        c.p[i][j]=Max(c.p[i][j],p[i][k]+b.p[k][j]);
            return c;
        }
    }b[N*4],c[N],ans;
    int rd(){
        int s=0,ff=1;
        char w=getchar();
        while(!isdigit(w))
            ff^=w=='-',w=getchar();
        while(isdigit(w))
            s=s*10+w-'0',w=getchar();
        return ff?s:-s;
    }
    void Add(int x,int y){
        a[++cnt].nxt=h[x];
        a[cnt].to=y;
        h[x]=cnt;
    }
    void Dfs(int x,int w){ int y; siz[x]=1;
        for(int i=h[x];i;i=a[i].nxt){
            y=a[i].to;
            if(y==w) continue ; fa[y]=x;
            dep[y]=dep[x]+1; Dfs(y,x);
            siz[x]+=siz[y];
            if(siz[y]>siz[Son[x]])
                Son[x]=y;
        }
    }
    void Dfss(int x,int w){ int y;
        dfn[++tot]=x; sfn[x]=tot;
        if(Son[x])
            Top[Son[x]]=Top[x],Dfss(Son[x],x),End[x]=End[Son[x]];
        else End[x]=x;
        for(int i=h[x];i;i=a[i].nxt){
            y=a[i].to;
            if(y==w||y==Son[x]) continue ;
            Top[y]=y; Dfss(y,x);
        }
        
    }
    void Dfs_dp(int x,int w){ int y;
        f[x][0]=g[x][0]=0; f[x][1]=g[x][1]=d[x];
        for(int i=h[x];i;i=a[i].nxt){
            y=a[i].to; if(y==w) continue ; Dfs_dp(y,x);
            f[x][0]+=Max(f[y][0],f[y][1]),f[x][1]+=f[y][0];
            if(y!=Son[x])
                g[x][0]+=Max(f[y][0],f[y][1]),g[x][1]+=f[y][0];
        }
    }
    void Build(int x,int l,int r){
        if(l==r){
            b[x].p[1][1]=g[dfn[l]][0];
            b[x].p[1][2]=g[dfn[l]][0];
            b[x].p[2][1]=g[dfn[l]][1];
            b[x].p[2][2]=-1e8;
            c[dfn[l]]=b[x]; return ;
        }
        int mid=(l+r)>>1;
        Build(x<<1,l,mid);
        Build(x<<1|1,mid+1,r);
        b[x]=b[x<<1]*b[x<<1|1];
    }
    void Update(int x,int l,int r,int s){
        if(l==r){
            b[x]=c[dfn[l]]; return ;
        }
        int mid=(l+r)>>1;
        if(s<=mid) Update(x<<1,l,mid,s);
        else Update(x<<1|1,mid+1,r,s);
        b[x]=b[x<<1]*b[x<<1|1];
    }
    Num Query(int x,int l,int r,int L,int R){
        if(L<=l&&r<=R) return b[x];
        int mid=(l+r)>>1;
        if(mid>=R) return Query(x<<1,l,mid,L,R);
        else if(mid<L) return Query(x<<1|1,mid+1,r,L,R);
        else return Query(x<<1,l,mid,L,R)*Query(x<<1|1,mid+1,r,L,R);
    }
    int main(){
        int x,y,z; Num A,B;
        n=rd(); m=rd();
        for(int i=1;i<=n;i++)
            d[i]=rd();
        for(int i=1;i<n;i++)
            x=rd(),y=rd(),Add(x,y),Add(y,x);
        dep[1]=1; Dfs(1,0);
        Top[1]=1; Dfss(1,0);
        Dfs_dp(1,0); Build(1,1,n);
        while(m--){
            x=rd(),y=rd();
            c[x].p[2][1]+=y-d[x]; d[x]=y;
            while(x){
                A=Query(1,1,n,sfn[Top[x]],sfn[End[x]]);
                Update(1,1,n,sfn[x]);
                B=Query(1,1,n,sfn[Top[x]],sfn[End[x]]);
                x=fa[Top[x]];
                c[x].p[1][1]+=Max(B.p[1][1],B.p[2][1])-Max(A.p[1][1],A.p[2][1]);
                c[x].p[1][2]+=Max(B.p[1][1],B.p[2][1])-Max(A.p[1][1],A.p[2][1]);
                c[x].p[2][1]+=B.p[1][1]-A.p[1][1];
            }
            A=Query(1,1,n,sfn[1],sfn[End[1]]);
            printf("%d
    ",Max(A.p[1][1],A.p[2][1]));
        }
        return 0;
    }
    "动态DP"&动态树分治

    今天就这样吧,看了几题比较简单就没写了,明天再说。

  • 相关阅读:
    [哈工大操作系统]一、环境配置
    [算法笔记]带权并查集
    C++:Reference to non-static member function must be called
    [算法笔记]并查集
    C++:string.size()比较问题
    [算法笔记]二分总结
    【LeetCode每日一题】2020.10.15 116. 填充每个节点的下一个右侧节点指针
    1Manjaro的安装
    【《你不知道的JS(中卷②)》】一、 异步:现在与未来
    【LeetCode每日一题】2020.7.14 120. 三角形最小路径和
  • 原文地址:https://www.cnblogs.com/manmanjiangQwQ/p/13298789.html
Copyright © 2011-2022 走看看