zoukankan      html  css  js  c++  java
  • 四边形不等式

    前言

    之前写过一篇关于斜率优化的文章:Link

    下面介绍另一种利用决策单调性来转移优化的方法。

    算法简介

    如果子问题的数目为 (Theta(n^2)),每个子问题需要用到 (Theta(n)) 个子问题的结果来转移,那么我们称它为 2D/1D 的问题。

    一般来说,它的状态转移方程是这样子的:

    [dp[i][j]=min_{ileq k<j}{dp[i][k]+dp[k+1][j]}+val(i,j) ]

    不难发现,这是区间 DP 的常用方程。

    如果直接进行状态转移,那么时间复杂度为 (Theta(n^3))

    但是,如果 (val(i,j)) 存在某些特殊的性质,我们就可以用四边形不等式对其进行优化。

    这里先介绍一下四边形不等式:

    • 定义 (1):如果 (forall lleq l'leq r'leq r,f(l,r')+f(l',r)leq f(l',r')+f(l,r)),则称函数 (f) 满足四边形不等式

    • 定义 (2):如果 (forall lleq l'leq r'leq r,f(l',r')leq f(l,r)),则称函数 (f) 满足区间包含单调性

    下面详细介绍一下关于它的一些小性质:

    • 性质 (1):函数 (f) 满足四边形不等式,当且仅当 (forall i<j,f(i+1,j)+f(i,j+1)leq f(i,j)+f(i+1,j+1))

    • 性质 (2):在上面的状态转移方程中,若 (val) 满足区间包含单调性和四边形不等式,则 (dp) 也满足四边形不等式。

    • 性质 (3):在上面的状态转移方程中,设 (dp) 的决策为 (s),且 (dp) 满足四边形不等式,则 (s[i][j-1]leq s[i][j]leq s[i+1][j])

    关于上面性质的证明如下:

    • 性质 (1) 的证明:

    用数学归纳法解释,比较简单。

    • 性质 (2) 的证明:

    考虑对区间长度归纳。

    当区间长度为 (1,2) 时,不难证明四边形不等式成立。

    假设当区间长度 (<j-i+1) 时,四边形不等式成立,下面证明当区间长度 (=j-i+1) 时成立。

    相当于去证明这个式子:(dp[i][j]+dp[i+1][j+1]leq dp[i][j+1]+dp[i+1][j])

    (dp[i+1][j]) 是由 (k=x) 转移得到,(dp[i][j+1]) 是由 (k=y) 转移得到,不妨设 (xleq y)

    这里先证明一下:(dp[x+1][j]+dp[y+1][j+1]leq dp[x+1][j+1]+dp[y+1][j])

    事实上,由归纳假设我们知道:(dp[x+1][j]-dp[x+1][j+1]leq dp[x+2][j]-dp[x+2][j+1])

    以此类推即可得到上面我们想证的式子是成立的。

    此时将 (k=x) 代入 (dp[i][j]),将 (k=y) 代入 (dp[i+1][j+1]) 得到:

    [egin{align*}& dp[i][j]+dp[i+1][j+1]\&leq dp[i][x]+dp[x+1][j]+val(i,j)+dp[i+1][y]+dp[y+1][j+1]+val(i+1,j+1)\&leq dp[i][x]+dp[x+1][j+1]+val(i,j+1)+dp[i+1][y]+dp[y+1][j]+val(i+1,j)\&leq dp[i][j+1]+dp[i+1][j]end{align*} ]

    得证!

    • 性质 (3) 的证明:

    先证明:(s[i][j]leq s[i+1][j])

    我们设 (dp[i][j]) 是由 (s[i][j]=d) 转移得到的。

    (h[k][i][j]=dp[i][k]+dp[k+1][j],forall k<d)

    那么有 (dp[i][j]leq h[k][i][j]),接下来只需证明:(dp[i+1][j]leq h[k][i+1][j]) 即可。

    [egin{align*}& (dp[i+1][j]-h[k][i+1][j])-(dp[i][j]-h[k][i][j])\&=(dp[i+1][j]+h[k][i][j])-(h[k][i+1][j]+dp[i][j])\&leq (dp[i+1][d]+dp[d+1][j]+dp[i][k]+dp[k+1][j])-(dp[i+1][k]+dp[k+1][j]+dp[i][d]+dp[d+1][j])\&=(dp[i+1][d]+dp[i][k])-(dp[i+1][k]+dp[i][d])end{align*} ]

    由于 (dp) 满足四边形不等式,故我们有:(forall i+1leq k<d,dp[i+1][d]+dp[i][k]leq dp[i+1][k]+dp[i][d])

    因此,(dp[i+1][j]-h[k][i+1][j]leq dp[i][j]-h[k][i][j]leq 0)

    于是,(s[i][j]leq s[i+1][j])

    同理可得:(s[i][j-1]leq s[i][j])

    得证!

    例题讲解

    下面提供一道较为基础的题目:

    例题1:[NOI1995]石子合并

    (Large{ ext{Description:}})

    在一个圆形操场的四周摆放 (n) 堆石子,现要将石子有次序地合并成一堆。规定每次只能选相邻的两堆合并成新的一堆,并将新的一堆的石子数记为该次合并的得分。

    需要计算出将 (n) 堆石子合并成一堆的最小得分和最大得分。

    数据范围:(nleq 100)

    (Large{ ext{Solution:}})

    首先,这题可以想到用区间 DP 求解,再加上断环成链的思想,我们可以考虑到上面说的 2D/1D 的常规状态转移方程:

    最大值比较简单,直接取两个端点的最大值即可;

    最小值需要用下面的状态转移方程来实现:

    [dp[i][j]=min_{ileq k<j}{dp[i][k]+dp[k+1][j]}+sum[j]-sum[i-1] ]

    这里按照上面的证明过程,不难得到 (dp) 是满足四边形不等式的。

    那么利用性质 (2,3) 就可以写出下面的代码……(四边形不等式的优化大致上就是一个模板,会灵活应用即可)

    时间复杂度:(Theta(n^2))

    (Large{ ext{Code:}})

    #include<bits/stdc++.h>
    #define Re register
    using namespace std;
    
    const int N=205;
    
    int n,a[N],sum[N];
    
    int DP[N][N];
    
    int s[N][N],dp[N][N];
    
    inline int rd()
    {
    	char ch=getchar();
    	int x=0,f=1;
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    
    int main()
    {
    	scanf("%d",&n);
    	for(Re int i=1;i<=n;i++)
    	{
    		a[i]=rd();
    		a[n+i]=a[i];
    	}
    	for(Re int i=1;i<=n+n;i++)
    	{
    		sum[i]=sum[i-1]+a[i];
    		s[i][i]=i;
    	}
    	for(Re int i=n+n;i>0;i--)
    	{
    		for(Re int j=i+1;j<=n+n;j++)
    		{
    			DP[i][j]=max(DP[i+1][j],DP[i][j-1])+sum[j]-sum[i-1];
    			int Mindp=0x3f3f3f3f,Mins;
    			for(Re int k=s[i][j-1];k<=s[i+1][j];k++)
    			{
    				if(Mindp>dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1])
    				{
    					Mindp=dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1];
    					Mins=k;
    				}
    			}
    			dp[i][j]=Mindp;
    			s[i][j]=Mins;
    		}
    	}
    	int ans1=0x3f3f3f3f,ans2=0;
    	for(Re int i=1;i<=n+1;i++)
    	{
    		ans1=min(ans1,dp[i][i+n-1]);
    		ans2=max(ans2,DP[i][i+n-1]);
    	}
    	printf("%d
    %d",ans1,ans2);
    	return 0;
    }
    

    练习题

    1. 山区建小学

    2. [IOI2000]邮局

  • 相关阅读:
    Asp.net 动态为TreeView创建结点
    JQuery 获取鼠标位置
    几个常见的“算法”小程序
    C# 99乘法表
    C#打印一个等腰倒三角形
    JQuery点击行(tr)实现checkBox选中,反选时移除和添加样式.
    使用FIFO策略缓存对象
    Firefly
    when you say nothing at all
    linux tar 压缩解压命令
  • 原文地址:https://www.cnblogs.com/kebingyi/p/14347709.html
Copyright © 2011-2022 走看看