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

    继续复习动规。

    单调队列优化DP(没错还是这个)

    第一道,[SCOI2010]股票交易看起来限制多,其实就是纸老虎。

    可设f[i][j]指第i天拥有j张股票所赚的最大钱数,所以f[i][j]可为负,所以初值设为-inf。

    四种情况:1.什么都不买,f[i][j]=max{f[i-1][j]}

    2.第一次买,f[i][j]=max{-j*APi}(0<=j<=ASi

    3.之后的买,f[i][j]=max{f[i-W-1][k]+k*APi}-j*APi(j-ASi<=k<j)

    4.卖,f[i][j]=max{f[i-W-1][k]+k*BPi}-j*BPi(j<k<=j+BSi

    前面两种直接O(n2),后面两种用单调队列优化,第三种从前往后,第四种从后往前,到这里就不难写了。

    数据结构优化DP

    第一道,[SCOI2014]方伯伯的玉米田

    首先得发现一个性质,每次修改的区间的右端点一定是最后一个点,证明如下:

    对于这个区间外的左边的玉米,其最长不下降子序列会增大或不变。

    而对于右边的玉米,其最长不下降子序列会减小或不变。故要减小右边的玉米数,当然为0最好。证明毕。

    设f[i][j]指当前这个玉米被拔高后的高度为i且这个玉米被j个拔高区间覆盖的最长不下降子序列长度。

    可得:f[i][j]=max{f[k][l]}+1(k<=i,l<=j)。单做要O(n4),但是前缀取最大值我们可以想到树状数组。

    于是定义一个二维树状数组(就是我代码里的f数组),优化后的时间复杂度为O(n2*logn*logn);

    这题想到性质很重要,数组的定义也很难想到(I am vegetable),再多写点同类型的题吧。

    记得j要倒着枚举避免重复计算,代码的具体实现如下:

    #include<bits/stdc++.h>
    #define Lowbit(i) (i&(-i))
    using namespace std;
    const int N=1e4+1e3;
    int n,k,ans,d[N],f[5600][510];
    int Max(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 Query(int x,int y){
        int ss=0;
        for(int i=x;i;i-=Lowbit(i))
            for(int j=y;j;j-=Lowbit(j))
                ss=Max(ss,f[i][j]);
        return ss;
    }
    void Change(int x,int y,int ss){
        for(int i=x;i<=5500;i+=Lowbit(i))
            for(int j=y;j<=k+1;j+=Lowbit(j))
                f[i][j]=Max(ss,f[i][j]);
    }
    int main(){
        int x; n=rd(),k=rd();
        for(int i=1;i<=n;i++)
            d[i]=rd();
        for(int i=1;i<=n;i++)
            for(int j=k;j>=0;j--){
                x=Query(d[i]+j,j+1)+1;
                if(x>ans) ans=x;
                Change(d[i]+j,j+1,x);
            }
        printf("%d
    ",ans);
        return 0;
    }
    [SCOI2014]方伯伯的玉米田

    第二道,[BalticOI 2017] Toll很有意思的题。

    题目给出了(b/k)=1+(a/k)(括号为下取整号)。所以我们从这个方面去思考。

    在思考了几个世纪后,我们发现(i/k)相同的有很多个i,将这些i当做一整块,于是n个地点就被分为ceil(n/k)个块。

    然后又可以发现a->b恰好是一个块中的点到后面的一个块中的点,于是定义f[i]为i到终点的最短路,就可以用矩阵来转移状态。

    | disx->a    disx->b    disx->c |         | f[a] |       | f[x] |

    | disy->a    disy->b    disy->c |    *   | f[b] |   =   | f[y] |

    | disz->a    disz->b    disz->c |         | f[c] |       | f[z] |    (这里的*不是乘法,是新定义的运算C[i][j]=max{A[i][k]+B[k][j]})

    然后想到了什么?没错,就是我们的动态dp。接下来就很简单了,把dis存下来放到线段树里,每次查询“l的块~r的块”的矩阵连乘。

    又因为f[终点]=0而其他的f为无穷大,所以答案就是矩阵中的某个值。(由l,r决定哪行哪列)。

    最后记得判断-1。代码的具体实现如下:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=5e4+1e3,M=7e8;
    int k,n,m,q,t;
    int Min(int x,int y){return x<y?x:y;}
    struct Num{
        int p[6][6];
        Num(){
            memset(p,127/3,sizeof(p));
        }
        inline Num operator*(Num b){
            Num c;
            for(int i=1;i<=k;i++)
                for(int j=1;j<=k;j++)
                    for(int l=1;l<=k;l++)
                        c.p[i][j]=Min(c.p[i][j],p[i][l]+b.p[l][j]);
            return c;
        }
    }d[N],f[N*4];
    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 Build(int x,int l,int r){
        if(l==r){
            f[x]=d[l]; return ;
        }
        int mid=(l+r)>>1;
        Build(x<<1,l,mid);
        Build(x<<1|1,mid+1,r);
        f[x]=f[x<<1]*f[x<<1|1];
    }
    Num Query(int x,int l,int r,int L,int R){
        if(L<=l&&r<=R) return f[x];
        int mid=(l+r)>>1;
        if(mid>=R) return Query(x<<1,l,mid,L,R);
        else if(mid+1<=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;
        k=rd(),n=rd(),m=rd(),q=rd();t=ceil(1.0*n/k);
        while(m--){
            x=rd(),y=rd(),z=rd();
            d[y/k].p[x%k+1][y%k+1]=z;
        }
        Build(1,1,t-1);
        while(q--){
            x=rd(),y=rd();
            if(x/k+1<=y/k) z=Query(1,1,t-1,x/k+1,y/k).p[x%k+1][y%k+1];// 别忘了这里的if 
            else z=M+1;    z>M?printf("-1
    "):printf("%d
    ",z);
        }
        return 0;
    }
    [BalticOI 2017] Toll

    状压DP

    第一道,花园

    由m<=5很容易想起状压dp,于是设f[i][j]为当前做到第i盆花且最后m盆花的状态为j的方案数。

    这个定义非常快乐,然后我们快乐地转移。那么怎么处理环形呢?

    我们只需要每次给一种状态j的f[m][j]赋值为1,并做到f[n+m][j],将答案累加即可。

    再就快乐地提交,然后快乐地WA(不是

    发现n有1015,考虑快速幂。f的转移可以用邻接矩阵代替,因此需要矩阵快速幂。

    到这里应该就没问题了,上代码:

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int N=1e5+1e4,P=1e9+7;
    int m,k,ans,tot,f[41],g[41]; ll n;
    struct Num{
        int p[41][41];
        Num(){
            memset(p,0,sizeof(p));
        }
    }b,c;
    ll rd(){
        ll 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 Dfs(int x,int s,int d){
        if(x>m){
            if(d<=k) f[s]=++tot,g[tot]=s; return ;
        }
        Dfs(x+1,s<<1,d);
        Dfs(x+1,s<<1|1,d+1);
    }
    Num Chen(Num A,Num B){
        Num C;
        for(int i=1;i<=tot;i++)
            for(int j=1;j<=tot;j++)
                for(int k=1;k<=tot;k++)
                    C.p[i][j]=(C.p[i][j]+(ll)A.p[i][k]*B.p[k][j]%P)%P;
        return C;
    }
    int main(){ int x;
        n=rd(); m=rd(); k=rd(); Dfs(1,0,0);
        for(int i=1;i<=tot;i++) c.p[i][i]=1;
        for(int i=1;i<=tot;i++){
            x=(g[i]>>1)+(1<<(m-1));
            if(f[x]) b.p[f[x]][i]=1;
            x=(g[i]>>1);
            if(f[x]) b.p[f[x]][i]=1;
        }
        while(n){
            if(n&1) c=Chen(b,c);
            b=Chen(b,b); n>>=1;
        }
        for(int i=1;i<=tot;i++)
            ans=(ans+c.p[i][i])%P;
        printf("%d
    ",ans);
        return 0;
    }
    花园

    最后做了道小水题[EER2]谔运算,对每个数位进行讨论就行了。行吧就这样,明天再说。拜拜。

  • 相关阅读:
    ubuntu下配置django+apache+mysql+mod_python+Python
    Makefile 学习
    一个查重算法的多种实现思路
    MongoDB基础
    基于ASP.NET MVC 3的企业应用项目总结
    CruiseControl.Net持续集成平台搭建总结
    Cnblogs Start
    JavaScript中的this
    Entity Framework 4.0 的一个bug :DefaultValue问题
    .Net平台下的B/S开发框架
  • 原文地址:https://www.cnblogs.com/manmanjiangQwQ/p/13303726.html
Copyright © 2011-2022 走看看