【知识点】四边形不等式&决策单调性

四边形不等式:

考虑形如$dp(i,j)=min\{dp(i,k)+dp(k,j)+w(i,j)\}$的dp。(若为max则把下文大小关系取反即可)

定义:若二元函数w满足$\forall a<b\leq c<d,w(a,c)+w(b,d)\leq w(b,c)+w(a,d)$,则称其满足四边形不等式。

几何意义:

以x为编号,y为自变量,$w(x,y)$为因变量,把n个$w(x)$的图像画到一个坐标系里。

再令$g(x,y)=w(x,y)-w(x-1,y)$,则只要$\forall x,g(x)$关于y递减即可。

推论1:若$w(i,j)$满足四边形不等式且$\forall a<b\leq c<d,w(b,c)\leq w(a,d)$(包含单调),则二元函数$dp(i,j)$也满足四边形不等式。

推论2:定义$k(i,j)$为使$dp(i,j)$取到最值的k,则在满足引理1的条件下,有$k(i,j-1)\leq k(i,j)\leq k(i+1,j)$。

两推论证明:见https://www.cnblogs.com/a1b3c7d9/p/10984353.html。

应用:显然用引理2可以将需要$O(n^{3})$枚举k的问题变成$O(n^{2})$的。

 

决策单调性:

考虑形如$dp(i)=min\{dp(j)+w(j,i)\}$的dp。(max类似)

定义:一个dp满足决策单调性的充要条件是:对于两个决策点$x<y$,若在i处y优于x,则在$[i+1,n]$处y都优于x。

引理:若w满足四边形不等式,则dp满足决策单调性。

证明:

令$x<y<i<j$,其中y为i的最优决策点,则有$dp(y)+w(y,i)\leq dp(x)+w(x,i)$。

由四边形不等式,有$w(x,i)+w(y,j)\leq w(y,i)+w(x,j)$。

两式相加,消去相同项,有$dp(y)+w(y,j)\leq dp(x)+w(x,j)$,于是y在j处也优于x。

应用:

  • 单调栈/单调队列内二分维护决策单调性dp。
  • CDQ分治维护决策单调性dp。

 

代码(NOI2009诗人小G):

#include<bits/stdc++.h>
#define maxn 100005
#define maxm 35
#define inf 0x7fffffff
#define ll long double
#define rint register int
#define debug(x) cerr<<#x<<": "<<x<<endl
#define fgx cerr<<"--------------"<<endl
#define dgx cerr<<"=============="<<endl

using namespace std;
int n,L,p,G[maxn]; 
ll S[maxn],F[maxn];
char str[maxn][maxm];
struct node{int id,l,r;}Q[maxn];

inline int read(){
    int x=0,f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}

inline ll pw(ll a,int b){ll r=1;while(b)r=(b&1)?r*a:r,a=a*a,b>>=1;return r;}
inline ll getf(int a,int b){return F[b]+pw(abs(S[a]-S[b]+a-b-1-L),p);}
inline void print(int r){
    if(!r) return; 
    int l=G[r]; print(l);
    for(int i=l+1;i<=r;i++){
        if(i>l+1) printf(" "); int m=strlen(str[i]+1);
        for(int j=1;j<=m;j++) printf("%c",str[i][j]);
    }
    printf("\n");
}

int main(){
    //freopen("P1912_1.in","r",stdin);
    //freopen("1.out","w",stdout);
    int T=read();
    while(T--){
        n=read(),L=read(),p=read();
        for(rint i=1;i<=n;i++) 
            scanf("%s",str[i]+1),S[i]=strlen(str[i]+1)+S[i-1];
        int l=1,r=1; F[0]=0,Q[1]=(node){0,1,n};
        for(rint i=1;i<=n;i++){
            while(l<r){if(Q[l].r<i)l++;else break;}
            F[i]=getf(i,Q[l].id),G[i]=Q[l].id;
            while(l<=r){
                if(getf(Q[r].l,Q[r].id)>getf(Q[r].l,i)) r--;
                else{
                    //cout<<r<<endl;
                    int l1=Q[r].l,r1=Q[r].r,ans=0;
                    while(l1<=r1){
                        int mid=l1+r1>>1;
                        if(getf(mid,Q[r].id)>getf(mid,i)) ans=mid,r1=mid-1;
                        else l1=mid+1;
                    } 
                    if(ans) Q[r].r=ans-1,Q[++r]=(node){i,ans,n}; 
                    else if(Q[r].r<n){int tp=Q[r].r+1; Q[++r]=(node){i,tp,n};}
                    break;
                }
            }/*
            for(int k=l;k<=r;k++){
                cout<<Q[k].id<<" "<<Q[k].l<<" "<<Q[k].r<<endl;
            }
            cout<<endl;*/
        }
        if(F[n]>1e18) printf("Too hard to arrange\n");
        else printf("%.0Lf\n",F[n]),print(n);
        printf("--------------------\n");
    }
    return 0;
}
/*
1
4 9 2
brysj,
hhrhl.
yqqlm,
gsycl.
*/
诗人小G

 

posted @ 2020-07-23 14:44  Fugtemypt  阅读(410)  评论(0编辑  收藏  举报