• 几个dp的陈年老题


    真 陈年老题

    都是基础的dp优化

    主要是展现我基础薄弱,菜得抠脚

     

    1.四边形不等式

    四边形不等式:w[i][j]+w[i+1][j+1]<=w[i+1][j]+w[i][j+1]

    对于f[i][j]=f[x][k]+f[k+1][y]+w[x][y],若w同时满足区间包含单调性和四边形不等式,那么f也满足四边形不等式

    若f满足四边形不等式,具有决策单调性。设f[i][j]的决策点k为s[i][j],s[i+1][j]>=s[i][j]>=s[i][j-1]

    证明略。

    最最经典的例题,合并石子

    求最小值时因为满足四边形不等式,利用s的决策单调性即可n^2

    求最大值时不满足四边形不等式,但是f[i][j]=max(f[i+1][j],f[i][j-1])+w[i][j] 也是n^2

    llj给的证明(菜得连合并石子都不会的我):对于任意的四堆石子a,b,c,d不存在一种最优的合并方式是a,b合并,c,d合并再合并在一起。

    因为从a到d依次合并和从d到a依次合并中一定有一个比他更优。列式可得。

     1 //Achen
     2 #include<algorithm>
     3 #include<iostream>
     4 #include<cstring>
     5 #include<cstdlib>
     6 #include<vector>
     7 #include<cstdio>
     8 #include<queue>
     9 #include<cmath>
    10 #include<set>
    11 #include<map>
    12 #define For(i,a,b) for(int i=(a);i<=(b);i++)
    13 #define Rep(i,a,b) for(int i=(a);i>=(b);i--)
    14 const int N=203;
    15 typedef long long LL; 
    16 typedef double db;
    17 using namespace std;
    18 int n,a[N],sum[N],f[N][N],g[N][N],w[N][N],s[N][N];
    19 
    20 template<typename T> void read(T &x) {
    21     char ch=getchar(); x=0; T f=1;
    22     while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    23     if(ch=='-') f=-1,ch=getchar();
    24     for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
    25 }
    26 
    27 //#define DEBUG
    28 int main() {
    29 #ifdef DEBUG
    30     freopen("1.in","r",stdin);
    31     //freopen(".out","w",stdout);
    32 #endif
    33     read(n);
    34     For(i,1,n) { read(a[i]); sum[i]=sum[i-1]+a[i]; }
    35     For(i,1,n) { a[n+i]=a[i]; sum[n+i]=sum[n+i-1]+a[n+i]; }
    36     n*=2;
    37     For(i,1,n) For(j,1,n) w[i][j]=sum[j]-sum[i-1];
    38     Rep(i,n,1) For(j,i+1,n) g[i][j]=max(g[i+1][j],g[i][j-1])+w[i][j];
    39     memset(f,127/3,sizeof(f));
    40     For(i,1,n) f[i][i]=0;
    41     Rep(i,n,1) {
    42         s[i][i]=i;
    43         For(j,i+1,n) 
    44             For(k,s[i][j-1],s[i+1][j]) { 
    45                 if(k>=i&&k<j&&f[i][k]+f[k+1][j]+w[i][j]<f[i][j]) {
    46                     f[i][j]=f[i][k]+f[k+1][j]+w[i][j];
    47                     s[i][j]=k;
    48                 }
    49             }
    50     }
    51     n/=2;
    52     int ans1=f[1][n],ans2=g[1][n];
    53     For(i,1,n) ans1=min(ans1,f[i][i+n-1]),ans2=max(ans2,g[i][i+n-1]);
    54     printf("%d
    %d
    ",ans1,ans2);
    55     return 0;
    56 }
    View Code

     

    2.决策单调性和斜率优化

    非常非常经典的例题,玩具装箱

    列出dp方程:
    $f_i=min_{j=0}^{j<i}  f[j]+(i-j-1-L+sum[i]-sum[j])^2$

    1.
    易证代价函数满足四边形不等式,或打表发现,具有决策单调性
    维护单调队列,新进入一个决策点弹出队尾的一部分决策点然后在队尾二分即可。
    更新答案的时候弹出队首的部分用完的决策点
    注意决策点不一定入队。

    2.

    $x_j=sum_j+j$
    $y_j=x_j^2+f_j$
    $A_i=i+sum[i]-1-L$
    $f_i=A_i^2-2*A_i*x_j+y_j$
    若j优于k,则有
    $-2*A_i*x_j+y_j<-2*A_i*x_k+y_k$

    决策单调性,$j>k,x_j>x_k,y_j>y_k$

    $(y_j-y_k)/(x_j-x_k)<2*A_i$

    $上式为j,k(j>k) 两点的斜率,维护单调队列,队列中斜率单增即可$

    A单增,每次弹出队首斜率小于A的部分,剩下的第一个即为答案

    若加入新点i,使kj,ji斜率下降,对于每个A,若kj斜率大于A,则j不如k优,否则ji斜率一定小于A,j不如i优,那么一定可以弹出j,故维护斜率单增的队列是合法的。

     

     1 //Achen
     2 #include<algorithm>
     3 #include<iostream>
     4 #include<cstring>
     5 #include<cstdlib>
     6 #include<vector>
     7 #include<cstdio>
     8 #include<queue>
     9 #include<cmath>
    10 #include<set>
    11 #include<map>
    12 #define For(i,a,b) for(int i=(a);i<=(b);i++)
    13 #define Rep(i,a,b) for(int i=(a);i>=(b);i--)
    14 const int N=50007;
    15 typedef long long LL; 
    16 typedef double db;
    17 using namespace std;
    18 int n,ql,qr;
    19 LL L,sum[N],c[N],f[N];
    20 
    21 template<typename T> void read(T &x) {
    22     char ch=getchar(); x=0; T f=1;
    23     while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    24     if(ch=='-') f=-1,ch=getchar();
    25     for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
    26 }
    27 
    28 struct node {
    29     int x,pos;
    30     node(){}
    31     node(int x,int pos):x(x),pos(pos){}
    32 }que[N];
    33 
    34 LL pf(LL x) { return x*x; }
    35 int ck(int i,int j,int pos) { 
    36     return f[i]+pf((LL)pos-i-1-L+sum[pos]-sum[i])<=f[j]+pf((LL)pos-j-1-L+sum[pos]-sum[j]);
    37 }
    38 
    39 //#define DEBUG
    40 int main() {
    41 #ifdef DEBUG
    42     freopen("1.in","r",stdin);
    43     //freopen(".out","w",stdout);
    44 #endif
    45     read(n); read(L);
    46     For(i,1,n) { read(c[i]); sum[i]=sum[i-1]+c[i]; }
    47     ql=1; qr=1;
    48     que[ql]=node(0,1);
    49     For(i,1,n) {
    50         while(ql<qr&&que[ql+1].pos-1<i) ql++;
    51         int j=que[ql].x;
    52         f[i]=(f[j]+pf((LL)i-j-1-L+sum[i]-sum[j]));
    53         while(ql<=qr&&ck(i,que[qr].x,que[qr].pos)) qr--;
    54         if(ql>qr) que[++qr]=node(i,1);
    55         else {
    56             int l=que[qr].pos,r=n,pos=-1,j=que[qr].x;
    57             while(l<=r) {
    58                 int mid=((l+r)>>1);
    59                 if(ck(i,j,mid)) pos=mid,r=mid-1;
    60                 else l=mid+1;
    61             }
    62             if(pos!=-1) que[++qr]=node(i,pos);
    63         }
    64     }
    65     printf("%lld
    ",f[n]);
    66     return 0;
    67 }
    决策单调性
    //Achen
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<vector>
    #include<cstdio>
    #include<queue>
    #include<cmath>
    #include<set>
    #include<map>
    #define For(i,a,b) for(int i=(a);i<=(b);i++)
    #define Rep(i,a,b) for(int i=(a);i>=(b);i--)
    #define eps 1e-10
    const int N=50007;
    typedef long long LL; 
    typedef double db;
    using namespace std;
    int n,ql,qr,que[N];
    LL L,sum[N],c[N],f[N];
    db x[N],y[N];
    
    template<typename T> void read(T &x) {
        char ch=getchar(); x=0; T f=1;
        while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
        if(ch=='-') f=-1,ch=getchar();
        for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
    }
    
    db pf(db x) { return x*x; } 
    db get_xl(int a,int b) {
        return (y[a]-y[b])/(x[a]-x[b]);
    }
    LL calc(int j,int i) { return f[j]+pf((LL)i-j-1-L+sum[i]-sum[j]); } 
    
    //#define DEBUG
    int main() {
    #ifdef DEBUG
        freopen("1.in","r",stdin);
        //freopen(".out","w",stdout);
    #endif
        read(n); read(L);
        For(i,1,n) { read(c[i]); sum[i]=sum[i-1]+c[i]; }
        ql=1; que[qr=1]=0;
        For(i,1,n) {
            db A=i+sum[i]-1-L;
            while(ql<qr&&get_xl(que[ql+1],que[ql])<A*2.0) ql++;
            f[i]=calc(que[ql],i);
            x[i]=sum[i]+i;
            y[i]=pf(sum[i]+i)+f[i];
            while(ql<qr&&get_xl(i,que[qr])+eps<get_xl(que[qr],que[qr-1])) qr--;
            que[++qr]=i;
        }
        printf("%lld
    ",f[n]);
        return 0;
    }
    斜率优化

     

    3.凸包优化

    对于f[i]=x[j]*a[i]+y[j]*b[i]这样的转移方程

    若决策直线斜率满足单调性则可以做到O(n)

    否则需要用数据结构维护凸包,

    平衡树维护凸包的模板题货币兑换

     

    第j天能购买的A,B券

    $A:x=(Rt_k*f_j)/(Rt_k*A_k+B_k)$

    $B:y=f_j/(Rt_k*A_k+B_k)$

    $f_i=MAX(x*B_i+y*A_i)$

    设$t_i=max_{j<=i}(f_j)$

    $w_k=Rt_k*A_k+B_k$

    $f_i=MAX(t_k*(B_i/W_k+Rt_k*A_i/W_k))$

    $x_i=t_i/W_i,y_i=x_i*Rt_i$

    $f_i=MAX(x_k*B_i+y_k*A_i)$

    $设p=x_k*B_i+y_k*A_i$

    $y_k=-B_i/A_i*x_k+p/A_i$

    A>0,p最大即纵截距最大,用splay维护上凸壳即可

    一开始不知道怎么着脑抽硬要维护右上凸壳,有猫病吧我

    码力是真的弱,主要就是很多地方没有想清楚就开敲,到底什么时候需要弹什么点
    然后代码要写得具有可读性,写太丑是真的自己都看不下去,bug根本de不出来

    错误示范:80分的丑陋的代码

     int y=pre(i),z=nxt(i);
            //if(!z&&y&&dcmp((q[i].y-q[y].y)/(q[i].x-q[y].x))>=0) { del(i); continue; }
            //else 
            if(!y&&z&&dcmp((q[z].y-q[i].y)/(q[z].x-q[i].x))>=0) { del(i); continue; }
            else if(y&&z&&dcmp(cross(q[z]-q[y],q[i]-q[y]))<=0) { del(i); continue; }
            for(;;) {
                y=pre(i); if(!y) break;
                db k=(q[i].y-q[y].y)/(q[i].x-q[y].x);
                if(!q[y].slop||(dcmp(k)<0&&dcmp(q[y].slop-k)>0)) break;
                del(y);
            }
            j=pre(i);
            if(!j) q[i].slop=0,q[i].lz=1;
            else q[i].slop=(q[i].y-q[j].y)/(q[i].x-q[j].x);
            for(;;) {
                y=nxt(i); if(!y) break;
                db k=(q[y].y-q[i].y)/(q[y].x-q[i].x);
                if((dcmp(k)<0)&&dcmp(q[i].slop-k)>0) break;
                del(y);
            }
            y=nxt(i); if(y) q[y].lz=0,q[y].slop=(q[y].y-q[i].y)/(q[y].x-q[i].x);
    

     正确示范:优美的代码更不容易出错

    void check(int x) {
        int y=pre(x),z=nxt(x);
        int k=dcmp(cross(q[z]-q[y],q[x]-q[y]));
        if(y&&z&&k<=0) { del(x); return; } 
        for(;;) {
            y=pre(x);
            if(!y) { q[x].lslop=inf; break; } 
            db k=get_slop(q[x],q[y]);
            if(dcmp(k-q[y].lslop)<0) {
                q[y].rslop=q[x].lslop=k; break;
            } del(y);
        }
        for(;;) {
            z=nxt(x);
            if(!z) { q[x].rslop=-inf; break; }
            db k=get_slop(q[z],q[x]);
            if(dcmp(k-q[z].rslop)>0) {
                q[z].lslop=q[x].rslop=k; break;
            } del(z);
        }
    }
    

     完整代码:

      1 //Achen
      2 #include<algorithm>
      3 #include<iostream>
      4 #include<cstring>
      5 #include<cstdlib>
      6 #include<vector>
      7 #include<cstdio>
      8 #include<queue>
      9 #include<cmath>
     10 #include<set>
     11 #include<map>
     12 #define For(i,a,b) for(int i=(a);i<=(b);i++)
     13 #define Rep(i,a,b) for(int i=(a);i>=(b);i--)
     14 #define inf 1e9
     15 const int N=100007;
     16 typedef long long LL; 
     17 typedef double db;
     18 using namespace std;
     19 int n;
     20 db f[N],s,A[N],B[N],Rt[N],t[N],mx;
     21 
     22 template<typename T> void read(T &x) {
     23     char ch=getchar(); x=0; T f=1;
     24     while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
     25     if(ch=='-') f=-1,ch=getchar();
     26     for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
     27 }
     28 
     29 struct pt {
     30     db x,y,lslop,rslop;
     31     pt(){}
     32     pt(db x,db y):x(x),y(y){}
     33 }q[N];
     34 #define eps 1e-10
     35 pt operator -(const pt&A,const pt&B) { return pt(A.x-B.x,A.y-B.y); } 
     36 int dcmp(db x) { if(fabs(x)<=eps) return 0; return x>0?1:-1; }
     37 db cross(pt a,pt b) { return a.x*b.y-a.y*b.x; }
     38 
     39 int p[N],ch[N][2],rt;
     40 #define lc ch[x][0]
     41 #define rc ch[x][1]
     42 void rotate(int x) {
     43     int y=p[x],z=p[y],l=(x==ch[y][1]),r=(l^1);
     44     if(z) ch[z][y==ch[z][1]]=x; p[x]=z;
     45     ch[y][l]=ch[x][r]; p[ch[x][r]]=y;
     46     ch[x][r]=y; p[y]=x;
     47 }
     48 
     49 void splay(int x,int FA) {
     50     for(;p[x]!=FA;rotate(x)) {
     51         int y=p[x],z=p[y];
     52         if(z!=FA) ((x==ch[y][1])^(y==ch[z][1]))?rotate(x):rotate(y);
     53     } if(!FA) rt=x;
     54 }
     55 
     56 void insert(int id) {
     57     int x=rt,f=0,l=0;
     58     for(;;) {
     59         if(!x) {
     60             p[id]=f; if(f) ch[f][l]=id; else rt=x;
     61             splay(id,0); break;
     62         }
     63         if(dcmp(q[x].x-q[id].x)>0) f=x,x=lc,l=0;
     64         else f=x,x=rc,l=1;
     65     }
     66 }
     67 
     68 int pre(int x) {
     69     splay(x,0); x=lc;
     70     while(rc) x=rc;
     71     return x;
     72 }
     73 
     74 int nxt(int x) {
     75     splay(x,0); x=rc;
     76     while(lc) x=lc;
     77     return x;
     78 }
     79 
     80 void del(int x) {
     81     if(!lc) { 
     82         if(x==rt) rt=rc,p[rt]=0;
     83         else ch[p[x]][x==ch[p[x]][1]]=rc,p[rc]=p[x];
     84     }
     85     else if(!rc) {
     86         if(x==rt) rt=lc,p[rt]=0;
     87         else ch[p[x]][x==ch[p[x]][1]]=lc,p[lc]=p[x];
     88     }
     89     else {
     90         int y=pre(x);
     91         int z=nxt(x);
     92         splay(y,0);
     93         splay(z,y);
     94         p[ch[z][0]]=ch[z][0]=0;
     95     }
     96 }
     97 
     98 int find(db k) {
     99     for(int x=rt;x;) {
    100         int t1=dcmp(q[x].lslop-k),t2=dcmp(q[x].rslop-k);
    101         if(t1>=0&&t2<=0) return x;
    102         if(t1<0) x=lc;
    103         else x=rc;
    104     } return -1;
    105 }
    106 
    107 db get_slop(pt A,pt B) { return (A.y-B.y)/(A.x-B.x); }
    108 
    109 void check(int x) {
    110     int y=pre(x),z=nxt(x);
    111     int k=dcmp(cross(q[z]-q[y],q[x]-q[y]));
    112     if(y&&z&&k<=0) { del(x); return; } 
    113     for(;;) {
    114         y=pre(x);
    115         if(!y) { q[x].lslop=inf; break; } 
    116         db k=get_slop(q[x],q[y]);
    117         if(dcmp(k-q[y].lslop)<0) {
    118             q[y].rslop=q[x].lslop=k; break;
    119         } del(y);
    120     }
    121     for(;;) {
    122         z=nxt(x);
    123         if(!z) { q[x].rslop=-inf; break; }
    124         db k=get_slop(q[z],q[x]);
    125         if(dcmp(k-q[z].rslop)>0) {
    126             q[z].lslop=q[x].rslop=k; break;
    127         } del(z);
    128     }
    129 }
    130 
    131 //#define DEBUG
    132 int main() {
    133 #ifdef DEBUG
    134     freopen("1.in","r",stdin);
    135     freopen("my.out","w",stdout);
    136 #endif
    137     scanf("%d%lf",&n,&s);
    138     For(i,1,n) {
    139         scanf("%lf%lf%lf",&A[i],&B[i],&Rt[i]);
    140         int j=find(-B[i]/A[i]);
    141         if(i!=1) f[i]=q[j].x*B[i]+q[j].y*A[i];
    142         else f[i]=s; f[i]=max(f[i],f[i-1]);
    143         mx=max(mx,f[i]);
    144         q[i].x=mx/(Rt[i]*A[i]+B[i]); q[i].y=q[i].x*Rt[i];
    145         insert(i); check(i);
    146     }
    147     db ans=0;
    148     printf("%.3lf
    ",f[n]);
    149     return 0;
    150 }
    View Code

     

    4.多重背包的nm做法

    noip之前在长沙学的,印象不是很深,所以拿出来

    $f_i=MAX(sum_{k=0}^{min(i/w,c)}f[i-k*w]+k*val)$

    那么每次按模w分类,每一类可以用单调队列维护

     1 //Achen
     2 #include<algorithm>
     3 #include<iostream>
     4 #include<cstring>
     5 #include<cstdlib>
     6 #include<vector>
     7 #include<cstdio>
     8 #include<queue>
     9 #include<cmath>
    10 #include<set>
    11 #include<map>
    12 #define For(i,a,b) for(int i=(a);i<=(b);i++)
    13 #define Rep(i,a,b) for(int i=(a);i>=(b);i--)
    14 const int N=50007;
    15 typedef long long LL; 
    16 typedef double db;
    17 using namespace std;
    18 int n,W,w[N],v[N],c[N],que[N],ql,qr;
    19 LL f[2][N],ans;
    20 
    21 template<typename T> void read(T &x) {
    22     char ch=getchar(); x=0; T f=1;
    23     while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    24     if(ch=='-') f=-1,ch=getchar();
    25     for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
    26 }
    27 
    28 //#define DEBUG
    29 int main() {
    30 #ifdef DEBUG
    31     freopen("1.in","r",stdin);
    32     //freopen(".out","w",stdout);
    33 #endif
    34     read(n); read(W);
    35     For(i,1,n) {
    36         read(w[i]); read(v[i]); read(c[i]);
    37     }
    38     int o=0;
    39     For(i,1,n) {
    40         o^=1;
    41         memset(f[o],0,sizeof(f[o]));
    42         For(d,0,w[i]-1) {
    43             ql=1; qr=0;
    44             for(int j=0;j*w[i]+d<=W;j++) {
    45                 while(ql<=qr&&j-que[ql]>c[i]) ql++;
    46                 f[o][j*w[i]+d]=f[o^1][j*w[i]+d];
    47                 if(ql<=qr) 
    48                     f[o][j*w[i]+d]=max(f[o][j*w[i]+d],f[o^1][que[ql]*w[i]+d]+(j-que[ql])*v[i]);
    49                 while(ql<=qr&&f[o^1][que[qr]*w[i]+d]-que[qr]*v[i]<=f[o^1][j*w[i]+d]-j*v[i]) qr--;
    50                 que[++qr]=j;    
    51             }
    52         }
    53     }
    54     printf("%lld
    ",f[o][W]);
    55     return 0;
    56 }
    View Code

     

     

     

     

  • 相关阅读:
    搭建springCloud网关zuul
    springboot添加https
    SpringBoot上传文件,经过spingCloud-Zuul,中文文件名乱码解决办法
    springboot mongodb jpa常用方法整理
    springboot拦截中自动注入的组件为null问题解决方法
    springboot JPA mysql
    转载Spring Data JPA 指南——整理自官方参考文档
    springboot带有进度条的上传
    HTTP请求示例
    http的N种请求
  • 原文地址:https://www.cnblogs.com/Achenchen/p/9019818.html
Copyright © 2020-2023  润新知