• 复习动规(4)


    昨天忘记写了,今天带着昨天的份一起写了。

    斜率优化DP

    第一道,[HNOI2008]玩具装箱非常经典的斜率优化dp题。

    首先定义f[x]为将前x件玩具刚好全部放入容器中的最小费用。

    则状态转移方程即为f[i]=min{f[j]+(i-(j+1)+sum[i]-sum[j]-L)2}。(j<=i)

    使p[i]=sum[i]+i,使L++,原方程可转化为f[i]=min{f[j]+(p[i]-p[j]-L)2}

    展开括号,并将与j无关项移出大括号,得:f[i]=min{f[j]+p[j]2+2p[j]*L-2p[j]*p[i]}+p[i]2+L2-2p[i]*L

    然后我们就可以设b=f[i],k=p[i],x=2p[j],y=f[j]+p[j]2+2p[j]*L(这里我们忽略大括号外面的项,计算时加上即可)

    根据数形结合的思想,这题就变成了:图上有若干个点(x,y),每次有两个操作:

    1.加入一个新点,这个点的x要大于之前的所有点。

    2.给定一个斜率k,求过图上的点且斜率为k的直线的最小截距。

    然后就可以用下凸包维护了。(最大截距就用上凸包)

    说起来好像大部分斜率优化DP都是这样的,但是这题的k也是递增的,所以凸包用单调队列即可O(n)。

    代码如下:

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int N=5e4+1e3;
    int n,l,r; ll L,a[N],p[N],f[N];
    struct nod{
        ll x,y;
    }b[N],A;
    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;
    }
    double Qiuk(nod X,nod Y){
        return 1.0*(X.y-Y.y)/(X.x-Y.x);
    }
    int main(){
        n=rd(); L=rd()+1;
        for(int i=1;i<=n;i++)
            a[i]=rd(),p[i]=p[i-1]+a[i];
        for(int i=1;i<=n;i++) p[i]+=i;
        l=1,r=0; b[++r].x=0; b[r].y=0;
        for(int i=1;i<=n;i++){
            while(l<r&&Qiuk(b[l+1],b[l])<=p[i]) l++;
            f[i]=b[l].y-p[i]*b[l].x+(p[i]-L)*(p[i]-L);
            A.x=2*p[i]; A.y=f[i]+p[i]*p[i]+2*p[i]*L;
            while(l<r&&Qiuk(A,b[r])<Qiuk(b[r],b[r-1])) r--;
            b[++r]=A;
        }
        printf("%lld
    ",f[n]);
        return 0;
    }
    [HNOI2008]玩具装箱

    第二道,[NOI2019]回家路线这题要以列车的编号为下标。

    首先设f[i]为第i辆列车到站后的最小烦躁值,方程即为:

    f[i]=min{f[j]+A(pi-qj)2+B(pi-qj)+C+qi-qj}。(pi>=qj&&yj==xi

    拆方程和求凸包相信大家都会,这里我讲下如何满足限制条件。

    将上车和下车分为两个操作,上车时求f[i]的值,下车时将点放入单调队列。

    因为时间小可以用vector直接放入桶排中,这样时间上从大到小去做就可以满足pi>=qj了。

    yj==xi就可以再开n个vector作为单调队列,完美。

    需要注意这题的点之间的x可能相等,所以求斜率时记得判断x相等的情况。

    具体可以看我代码(代码可读性较差,望谅解):

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int N=1e5+1e4,M=1e6+1e5;
    int n,m,t,l[N],r[N];
    ll A,B,C,f[M],ans=4e18;
    struct nod{
        int x,y,w; ll p,q;
    }T,a[M]; vector<nod>P[41000],PP[41000];
    struct nodd{
        ll x,y;
    }D; vector<nodd>Q[N];
    int Max(int x,int y){return x>y?x:y;}
    ll Min(ll x,ll 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;
    }
    double Qiuk(nodd X,nodd Y){
        if(X.x==Y.x){
            if(X.y>Y.y) return 1e18;
            else return -1e18;
        }
        return 1.0*(X.y-Y.y)/(X.x-Y.x);
    }
    int main(){
        n=rd(); m=rd(); A=rd(); B=rd(); C=rd();
        for(int i=1;i<=n;i++) l[i]=0,r[i]=-1;
        for(int i=1;i<=m;i++){
            a[i].x=rd(),a[i].y=rd(),a[i].p=rd(),a[i].q=rd(),a[i].w=i;
            P[a[i].p].push_back(a[i]); PP[a[i].q].push_back(a[i]);
            t=Max(t,(int)a[i].q);
        } D.x=0; D.y=0; ++r[1]; Q[1].push_back(D);
        memset(f,127,sizeof(f));
        for(int i=0;i<=t;i++){
            for(int j=0;j<PP[i].size();j++){ T=PP[i][j];
                if(f[T.w]>4e18) continue ;
                D.x=2*A*T.q; D.y=f[T.w]+A*T.q*T.q-B*T.q-T.q;
                while(l[T.y]<r[T.y]&&Qiuk(Q[T.y][r[T.y]],Q[T.y][r[T.y]-1])>=Qiuk(D,Q[T.y][r[T.y]])) r[T.y]--,Q[T.y].pop_back();
                ++r[T.y]; Q[T.y].push_back(D);
            }
            for(int j=0;j<P[i].size();j++){ T=P[i][j];
                while(l[T.x]<r[T.x]&&Qiuk(Q[T.x][l[T.x]+1],Q[T.x][l[T.x]])<=T.p) l[T.x]++;
                if(l[T.x]<=r[T.x]) f[T.w]=Q[T.x][l[T.x]].y-Q[T.x][l[T.x]].x*T.p+A*T.p*T.p+B*T.p+C+T.q;
            }
        }
        for(int i=1;i<=m;i++)
            if(a[i].y==n) ans=Min(ans,f[i]);
        printf("%lld
    ",ans); return 0;
    }
    [NOI2019]回家路线

    决策单调性优化DP

    一道,[NOI2009]诗人小G

    乍一看方程为f[i]=min{f[j]+|i-(j+1)+sum[i]-sum[j]-L|P}。(j<=i)

    我还以为又是斜率优化的板子,这和上面的方程几乎一样啊。

    然后才发现哦吼完蛋不会这个有绝对值而且也拆不开,于是我们需要换个方法。

    根据打表发现它的决策单调,于是就可以O(n*logn)过了。

    数位DP

    第一道,不要62个人偏好记忆化搜索做数位dp,记忆化搜索的数位dp推荐一篇很好的博客

    emmmm不知道怎么讲,数位dp都是一个板子,大家看看代码应该就明白了。

    #include<bits/stdc++.h>
    using namespace std;
    int ct,a[9],f[9][2];
    int Dfs(int x,bool pre,bool lim){
        if(!x) return 1;
        if(!lim&&f[x][pre]!=-1) return f[x][pre];
        int p=(lim?a[x]:9),s=0;
        for(int i=0;i<=p;i++){
            if(i==4) continue ;
            if(pre&&i==2) continue ;
            s+=Dfs(x-1,i==6,lim&&(i==p));
        }
        if(!lim) f[x][pre]=s; return s;
    }
    int Query(int x){ ct=0;
        while(x) a[++ct]=x%10,x/=10;
        return Dfs(ct,0,1);
    }
    int main(){
        int l,r; memset(f,-1,sizeof(f));
        while(1){
            scanf("%d%d",&l,&r); if(!l&&!r) return 0;
            printf("%d
    ",Query(r)-Query(l-1));
        }
    }
    不要62

    第二道,F(x)这题稍微特殊点,f[x][s]的s存的是还剩s个可以减去。代码就不放了。

    第三道,[ZJOI2010]数字计数

    对于0~9中的每个数,我们分别求其出现的次数,而且函数要多带个参数判断有无前导0。

    第四道,[SCOI2014]方伯伯的商场之旅个人感觉这题比较难,完全不知道怎么对数位得到合并石子的最优策略。

    应该是先把所有石子都合并到第一堆,这个很好求。但很明显不是最优状态,于是我们需要依次枚举最优决策点

    因为决策点由一变到二,所以总值相当于减去2到n的总石子数且加上1的石子数,所以判断2到n的石子数减去1的石子数是否大于0就行了,大于0就可以让答案减去该值。

    再就决策点由二变到三,总值相当于减去3到n的总石子数且加上1到2的石子数......如此反复即可。代码如下:

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    int K,ct,a[55];
    ll f[55][1100];
    ll Dfs1(int x,ll s,int lim){
        if(!x) return s;
        if(!lim&&f[x][s]!=-1)
            return f[x][s];
        int p=lim?a[x]:K-1; ll sum=0;
        for(int i=0;i<=p;i++)
            sum+=Dfs1(x-1,s+(ll)i*(x-1),lim&&i==p);
        if(!lim) f[x][s]=sum; return sum;
    }
    ll Dfs2(int x,ll s,int lim,int k){
        if(s<0) return 0;
        if(!x) return s;
        if(!lim&&f[x][s]!=-1)
            return f[x][s];
        int p=lim?a[x]:K-1; ll sum=0;
        for(int i=0;i<=p;i++){
            if(x>=k) sum+=Dfs2(x-1,s+i,lim&&i==p,k);
            else sum+=Dfs2(x-1,s-i,lim&&i==p,k);
        }
        if(!lim) f[x][s]=sum; return sum;
    }
    ll Query(ll x){
        ct=0; ll ans=0;
        while(x)
            a[++ct]=x%K,x/=K;
        memset(f,-1,sizeof(f));
        ans=Dfs1(ct,0,1);
        for(int i=2;i<=ct;i++){
            memset(f,-1,sizeof(f));
            ans-=Dfs2(ct,0,1,i);
        }
        return ans;
    }
    int main(){
        ll l,r;
        scanf("%lld%lld%d",&l,&r,&K);
        printf("%lld
    ",Query(r)-Query(l-1));
        return 0;
    }
    [SCOI2014]方伯伯的商场之旅

    嘿,总算是弄完了,哈~。接下来估计不会再做动规了,会复习或学习别的知识点。大家一起加油吧!

  • 相关阅读:
    CSS color
    Python 集成开发环境(IDE)
    Flexible and Economical UTF-8 Decoder
    方程式组织EQUATION DRUG平台解析(提纲) —方程式组织系列分析报告之四
    Windows Server 2008 IE 无法调整安全级别
    蟑螂数据库
    MySQL OCP
    TCP协议疑难杂症全景解析
    数据库资源博客---小麦苗BEST
    C++并发实战 与多线程
  • 原文地址:https://www.cnblogs.com/manmanjiangQwQ/p/13332965.html
Copyright © 2020-2023  润新知