四边形不等式:
考虑形如$dp(i,j)=min{dp(i,k)+dp(k,j)+w(i,j)}$的dp。(若为max则把下文大小关系取反即可)
定义:若二元函数w满足$forall a<bleq 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<bleq 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(" "); } 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 "); else printf("%.0Lf ",F[n]),print(n); printf("-------------------- "); } return 0; } /* 1 4 9 2 brysj, hhrhl. yqqlm, gsycl. */