【学习笔记】四边形不等式优化 DP

决策单调性

  • 对于最优化问题 fi=minj=1i{w(j,i)} ,称 w(j,i) 为成本函数,参数为 i 的最优化问题称为问题 i ,记问题 i 对应的最小最优决策点为 opt(i) 。其中,我们假设成本函数 w(j,i) 可以 O(1) 计算。
  • 决策单调性指对于任意 i1<i2 均有 opt(i1)opt(i2)
  • 最常见的判断决策单调性的方法是通过四边形不等式。

四边形不等式

  • 在成本函数 w(x,y) 中,若对于任意 abcd 均有 w(a,c)+w(b,d)w(a,d)+w(b,c) 则称函数 w(x,y) 满足四边形不等式,简记为“交叉小于包含”。若等号恒成立,又称函数 w(x,y) 满足四边形恒等式。
  • 定理
    • 在满足四边形不等式的函数 w(x,y) 中,对于任意 a<b 均有 w(a,b)+w(a+1,b+1)w(a,b+1)+w(a+1,b)
      • 证明:用 a,a+1,b,b+1 代入定义中的 a,b,c,d 即可。
    • 若函数 w(x,y) 满足四边形不等式,则最优化问题 fi=minj=1i{w(j,i)} 满足决策单调性。
      • 证明
        • 对于任意 i1[1,n],j[1,opt(i1)1] ,由最优性均有 w(opti1,i1)w(j,i1)
        • 对于任意 i2[i1+1,n],j[1,opt(i1)1] ,由四边形不等式均有 w(j,i1)+w(opt(i1),i2)w(j,i2)+w(opt(i1),i1) ,移项得到 w(opt(i1),i2)w(opt(i1),i1)w(j,i2)w(j,i1)
        • 将两个不等式相加,得到 w(opt(i1),i2)w(j,i2) ,进而得到 opt(i1)opt(i2) ,即最优化问题 fi=minj=1i{w(j,i)} 满足决策单调性。
  • 四边形不等式优化利用的是状态转移方程中的决策单调性

一维线性 DP 的四边形不等式优化

基础知识

  • 对于形如 fi=minj=0i1{fj+val(j,i)} 的状态转移方程,若成本函数 val(x,y) 满足四边形不等式,则 fi=minj=0i1{fj+val(j,i)} 满足决策单调性。
    • 证明
      • 类比 w(x,y) 的证明方式,将 fj+val(j,i) 代入可得到 fj+val(j,i) 满足四边形不等式,进而推出 fi=minj=0i1{fj+val(j,i)} 满足决策单调性。
  • 常见的实现方式是分治和二分队列,二者各有优缺点,视情况决定使用哪种实现方法,时间复杂度均为 O(nlogn)
    • 分治
      • 求解所有状态只需要知道所有决策点。考虑分治过程中先求出中点处 mid 的决策点,更新区间范围内决策点的上下界。
    • 二分队列
      • 考虑维护 {opt} ,在枚举至 i 时,先前的 {opt} 一定形如 j1,,j1,j2,,j2,,jk(j1<j2<<jk) 。在插入 i 时要保证满足决策单调性和最优性,这就需要我们找到一个位置 pos 使得 [1,pos] 目前存储的决策都比 i 好,而 (pos,n] 目前存储的决策都比 i 差,接着将 (pos,n] 全部修改为 i
      • 因为 {opt} 中存在许多相同的元素,且暴力修改效率较低,可将相同元素试做一个块 (j,l,r) 表示 optlr=j
      • 同时,由于决策单调性的存在,我们没有必要保存 opt1i1 的部分来减少不必要的决策,单调队列就可以维护了。
      • 算法流程
        • (0) 插入 (0,1,n)
          • 如果存在 j=0 这个决策的话。
        • 对于任意 i[1,n] ,顺序执行以下操作。
        • (1) 取出队头并检查队头是否过时,即 head.r 是否 <i 。若已过时则删除队头,否则令 headl=i
        • (2) 令队头作为最优决策进行状态转移。
        • (3) 取出队尾,进行分讨。若对于 ftail.l 来看 itail.j 更优,则令 pos=taill ,删除队尾并本步骤重新开始;若对于 ftail.r 来看 itail.j 更差,则插入新决策 i ;否则,在 [taill,tailr] 上进行二分查找 pos 使得在此之前 tailj 更优,在此之后 i 更优,插入新决策 (i,pos,n)

    例题

luogu P3515 [POI2011] Lightning Conductor

  • 多倍经验: luogu P5503 [JSOI2016] 灯塔 | SP9070 LIGHTIN - Lightning Conductor

  • fi 表示使 ajai+fi|ij| 成立的最小非负整数,移项状态转移方程为 fi=maxj=1n{ajai+|ij|}=minj=1n{aiaj|ij|}

  • 1j<i 时,原式为 fi=minj=1i{aiajij}

    • w(x,y)=ayaxyx ,有 w(x,y) 满足四边形不等式。
      • 证明
        • 将式子拆开后等价于证明 2yxyx1yx+1 移项有 2yxyx1+yx+1 ,令 k=yx ,等价于证明 2kk1+k+1 ,左右平方后有 kk21 。原结论成立。
  • j=i 时,有 fi=max(fi,0)

  • i<j 时,将 {a} 翻转即可。

    点击查看代码
    ll a[500010],f[500010];
    deque<ll>qj,ql,qr;
    double w(ll x,ll y)
    {
    	return a[x]+sqrt(y-x);
    }
    void solve(ll n)
    {
    	qj.clear();
    	ql.clear();
    	qr.clear();
    	for(ll i=1;i<=n;i++)
    	{
    		while(qj.empty()==0&&qr.front()<i)
    		{
    			qj.pop_front();
    			ql.pop_front();
    			qr.pop_front();
    		}
    		if(qj.empty()==0)
    		{
    			ql.front()=i;
    			f[i]=max(f[i],-a[i]+((ll)(ceil(a[qj.front()]+sqrt(i-qj.front())))));
    		}
    		while(qj.empty()==0&&w(qj.back(),ql.back())<=w(i,ql.back()))
    		{
    			qj.pop_back();
    			ql.pop_back();
    			qr.pop_back();
    		}
    		if(qj.empty()==0)
    		{
    			if(w(qj.back(),n)<w(i,n))//保证有优于 i 的决策
    			{
    				ll l=ql.back(),r=n,mid;	
    				while(l<=r)
    				{
    					mid=(l+r)/2;
    					if(w(qj.back(),mid)<w(i,mid))
    					{
    						r=mid-1;
    					}
    					else
    					{
    						l=mid+1;
    					}
    				}
    				qr.back()=r;
    				qj.push_back(i);
    				ql.push_back(l);
    				qr.push_back(n);
    			}
    		}
    		else
    		{
    			qj.push_back(i);
    			ql.push_back(i);
    			qr.push_back(n);
    		}
    	}
    }
    int main()
    {
    	ll n,i;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    	}
    	solve(n);
    	reverse(a+1,a+1+n);
    	reverse(f+1,f+1+n);
    	solve(n);
    	reverse(f+1,f+1+n);
    	for(i=1;i<=n;i++)
    	{
    		cout<<f[i]<<endl;
    	}
    	return 0;
    }
    

luogu P1912 [NOI2009] 诗人小G

  • sumi 表示前 i 句诗加空格的长度,即 sumi=j=1i(|sj|+1)=i+j=1i|sj|

  • fi 表示将前 i 句诗排版后的最下协调度,状态转移方程为 fi=minj=0i1{fj+|sumisumjl1|p}

  • 拆掉绝对值后求导即可证明 f 是满足决策单调性的。

    • 还没学导数,具体证明先咕了。
  • 中途会炸 __int128_t 需要使用 long double 代替;手写快速幂来减小时间复杂度。

    点击查看代码
    ll sum[100010],ans[100010];
    long double f[100010];
    char s[100010][40];
    deque<ll>qj,ql,qr;
    long double qpow(long double a,ll b)
    {
    	long double ans=1;
    	while(b)
    	{
    		if(b&1)
    		{
    			ans=ans*a;
    		}
    		b>>=1;
    		a=a*a;
    	}
    	return ans;
    }
    long double  w(ll j,ll i,ll L,ll p)
    {
    	return f[j]+qpow(abs(sum[i]-sum[j]-L-1),p);
    }
    void print(ll x)
    {
    	if(x==0)
    	{
    		return;
    	}
    	else
    	{
    		print(ans[x]);
    		for(ll i=ans[x]+1;i<=x;i++)
    		{	
    			cout<<(s[i]+1);
    			if(i!=x)
    			{
    				cout<<" ";
    			}
    		}
    		cout<<endl;
    	}
    }
    int main()
    {
    	ll t,n,L,p,l,r,mid,i,j;
    	cin>>t;
    	for(j=1;j<=t;j++)
    	{
    		cin>>n>>L>>p;
    		memset(ans,0,sizeof(ans));
    		for(i=1;i<=n;i++)
    		{
    			cin>>(s[i]+1);
    			sum[i]=sum[i-1]+strlen(s[i]+1)+1;
    		}
    		qj.clear();
    		ql.clear();
    		qr.clear();
    		qj.push_back(0);
    		ql.push_back(1);
    		qr.push_back(n);
    		for(i=1;i<=n;i++)
    		{
    			while(qj.empty()==0&&qr.front()<i)
    			{
    				qj.pop_front();
    				ql.pop_front();
    				qr.pop_front();
    			}
    			if(qj.empty()==0)
    			{
    				ql.front()=i;
    				f[i]=w(qj.front(),i,L,p);
    				ans[i]=qj.front();
    			}
    			while(qj.empty()==0&&w(qj.back(),ql.back(),L,p)>=w(i,ql.back(),L,p))
    			{
    				qj.pop_back();
    				ql.pop_back();
    				qr.pop_back();
    			}
    			if(qj.empty()==0)
    			{
    				if(w(qj.back(),n,L,p)>w(i,n,L,p))
    				{
    					l=ql.back();
    					r=n;
    					while(l<=r)
    					{
    						mid=(l+r)/2;
    						if(w(qj.back(),mid,L,p)>w(i,mid,L,p))
    						{
    							r=mid-1;
    						}
    						else
    						{
    							l=mid+1;
    						}
    					}
    					qr.back()=r;
    					qj.push_back(i);
    					ql.push_back(l);
    					qr.push_back(n);
    				}
    			}
    			else
    			{
    				qj.push_back(i);
    				ql.push_back(i);
    				qr.push_back(n);
    			}
    		}
    		if(f[n]>1e18)
    		{
    			cout<<"Too hard to arrange"<<endl;
    		}
    		else
    		{
    			cout<<(ll)f[n]<<endl;
    			print(n);
    		}
    		cout<<"--------------------"<<endl;
    	}
    	return 0;
    }
    

LibreOJ 6039. 「雅礼集训 2017 Day5」珠宝 /「NAIPC2016」Jewel Thief

二维区间 DP 的四边形不等式优化

基础知识

  • 若对于任意的 abcd 均有 w(b,c)w(a,d) ,则称 w 对于区间包含关系具有单调性。
  • 对于形如 fi,j=mink=ij1{fi,k+fk+1,j+w(i,j)} 的状态转移方程,其中 fi,i=w(i,i)=0 ,若 w 满足四边形不等式和区间包含单调性,那么 f 也满足四边形不等式。
    • 证明
      • 考虑数学归纳法。
      • ji=1 时,有 f 满足四边形不等式。
        • fi,i=w(i,i)=0 ,有 {fi,j=fi,i+fj,j+w(i,j)=w(i,j)fi,j+1+fi+1,j=fi,i+2+fi+1,i+1=fi,i+2
        • fi,i+2 的最优决策是 i+1 ,则 fi,i+2=fi,i+1+fi+2,i+2+w(i,i+2)=w(i,i+1)+w(i,i+2)w(i,i+1)+w(i+1,i+2)=fi,i+1+fi+1,i+2=fi,j+fi+1,j+1
        • fi,i+2 的最优决策是 i ,则 fi,i+2=fi,i+fi+1,i+2+w(i,i+2)=w(i+1,i+2)+w(i,i+2)=w(i+1,i+2)+w(i,i+1)=fi+1,i+2+fi,i+1=fi+1,j+1+fi,j
        • ji=1 时,有 f 满足四边形不等式。
      • 假设当 ji<k 时,有 f 满足四边形不等式。
      • ji=k 时,设 x(x[i,j])fi,j+1 的最优决策, y(y[i+1,j1])fi+1,j 的最优决策,不妨设 i+1xy 。有 {fi,j+1+fi+1,j=fi,x+fx+1,j+1+w(i,j+1)+fi+1,y+fy+1,j+w(i+1,j)fi,j+fi+1,j+1fi,x+fx+1,j+w(i,j)+fi+1,y+fy+1,j+1+w(i+1,j+1) 。又因为 {w(i,j)+w(i+1,j+1)w(i,j+1)+w(i+1,j)fx+1,j+fy+1,j+1fx+1,j+1+fy+1,j ,有 fi,j+fi+1,j+1fi,j+1+fi+1,j 。故当 ji=k 时,有 f 满足四边形不等式。
      • f 满足四边形不等式。
  • 定理
    • 对于形如 fi,j=mink=ij1{fi,k+fk+1,j+w(i,j)} 的状态转移方程,其中 fi,i=w(i,i)=0 ,若 f 满足四边形不等式,那么对于任意 i<j 均有 opti,j1opti,jopti+1,j ,即满足二维决策单调性。
      • 证明
        • 由于 f 满足四边形不等式,对于任意的 k(i,opti,j] 均有 fi,k+fi+1,opti,jfi,opti,j+fi+1,k ,移项得到 fi,kfi,opti,jfi+1,kfi+1,opti,j
        • 又因为 fi,k+fk+1,jfi,opti,j+fopti,j+1,j ,移项有 fi,k+fk+1,jfi,opti,jfopti,j+1,j0
        • 对于 fi+1,j(fi+1,k+fk+1,j+w(i+1,j))(fi+1,opti,j+fopti,j+1,j+w(i+1,j))=fi+1,k+fk+1,jfi+1,opti,jfopti,j+1,j=(fi+1,kfi+1,opti,j)+(fk+1,jfopti,j+1,j)(fi,kfi,opti,j)+(fk+1,jfopti,j+1,j)=fi,k+fk+1,jfi,opti,jfopti,j+1,j0 ,说明 k 一定不比 opti,j 更优,故 opti,jopti+1,j
        • opti,j1opti,j 同理。

例题

luogu P1775 石子合并(弱化版)

  • sumi=j=1iai,w(l,r)=sumrsuml1 。显然有 w 满足四边形恒等式。

  • fl,r 表示把最初的第 lr 堆合并成一堆需要消耗的最少体力,状态转移方程为 fl,r=mink=lr1{fl,k+fk+1,r+w(l,r)} ,边界为 fi,i=0,opti,i=i

  • 由于 f 满足四边形不等式,故仅枚举 k[optl,r1,optl+1,r] ,时间复杂度为 O(l=1n1r=l+1n(optl+1,roptl,r1+1))=O(l=1n1(optl+1,nopt1,nl+nl))=O(n2)

    点击查看代码
    ll a[2010],sum[2010],f[2010][2010],opt[2010][2010];
    ll w(ll l,ll r)
    {
        return sum[r]-sum[l-1];
    }
    int main()
    {
        ll n,i,len,l,r,k;
        cin>>n;
        memset(f,0x3f,sizeof(f));
        for(i=1;i<=n;i++)
        {
            cin>>a[i];
            sum[i]=sum[i-1]+a[i];
            f[i][i]=0;
            opt[i][i]=i;
        }
        for(len=2;len<=n;len++)
        {
            for(l=1,r=l+len-1;r<=n;l++,r++)
            {
                for(k=opt[l][r-1];k<=opt[l+1][r];k++)
                {
                    if(f[l][k]+f[k+1][r]+w(l,r)<f[l][r])
                    {
                        f[l][r]=f[l][k]+f[k+1][r]+w(l,r);
                        opt[l][r]=k;
                    }
                }
            }
        }
        cout<<f[1][n]<<endl;
        return 0;
    }
    

满足四边形不等式的函数类

  • 若函数 w1(x,y)w2(x,y) 均满足四边形不等式(或区间包含单调性),则对于任意 k1,k20 均有 k1w1(x,y)+k2w2(x,y) 也满足四边形不等式(或区间包含单调性)。
  • 若对于函数 w(j,i) 存在函数 f(x),g(x) 使得 w(j,i)=f(i)g(j) ,则函数 w 满足四边形不等式。且当函数 f,g 单调递增时,函数 w 也满足区间包含单调性。
  • h(x) 是一个单调递增的凸函数,若函数 w(j,i) 满足四边形不等式和区间包含单调性,则复合函数 h(w(j,i)) 也满足四边形不等式和区间包含单调性。
  • h(x) 是一个凸函数,若函数 w(j,i) 满足四边形恒等式和区间包含单调性,则复合函数 h(w(j,i)) 也满足四边形不等式。
posted @   hzoi_Shadow  阅读(251)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
扩大
缩小
点击右上角即可分享
微信分享提示