可能需要莽一莽

决策单调性优化DP

大佬说是那就是,不狡辩

 

目录:

决策单调性的概念

四边形不等式 + 从四边形不等式到决策单调性

解决一维决策单调问题

(之前的部分在论证理论正确性;如果功利一点、只想解决问题的话,直接从上一部分的最后开始看就行了

一些题目

 


 

~ 决策单调性的概念 ~

 

假如对于某一个dp方程,$dp(i)$的最优转移是$dp(k)$,那么称$k$为$i$的决策点

而dp方程满足决策单调性指的是,决策点$k$随着$i$的增大保持单调不减(二维的情况稍微复杂一点,见下面的四边形不等式推决策单调性)

 

感觉有点类似斜率优化?

我们维护的下凸包,斜率也满足单调不减;所以如果斜率不等式的右侧是单调不减的(即决策时只需要向栈顶走、不需要在栈中二分),那么决策点也是单调不减的

所以绝大多数的斜率优化也满足决策单调性

 


 

~ 四边形不等式 ~

 

一维状态dp的决策单调性比较直观,二维状态的就没那么显然了;需要借助其他的东西进行分析

而四边形不等式正是推出决策单调性的有力工具(虽然最后判断决策单调性都是打表)

四边形不等式一般满足这样的格式:

对于函数$f$和$\forall a,b,c,d$,且$a<b<c<d$,有

\[f(a,c)+f(b,d)\leq f(a,d)+f(b,c)\]

即 相交区间之和 $\leq$ 包含区间之和,那么函数$f$满足四边形不等式

 

~ 四边形不等式到决策单调性 ~

 

拿一道具体的题目来说明吧

比如:Luogu P1880 (石子合并,$NOI1995$)

这道题目中,求最小得分满足决策单调性;最大得分却不满足(打表看出来的,没有证出来...)

计算最小得分,可以通过以下dp方程解决:记$dp(i,j)$表示将第$i\text{~}j$堆合并到一起的最小得分,$w(i,j)$表示第$i\text{~}j$堆的石子总数

则有$dp(i,j)=min\{dp(i,k)+dp(k+1,j)+w(i,j)\}$,其中$i<j$

 

一般来说,利用四边形不等式证明决策单调性的步骤大概是:

   1. 证明$w(i,j)$满足四边形不等式

   2. 证明$dp(i,j)$满足四边形不等式

   3. 证明$dp(i,j)$满足决策单调性

 

1. 证明$w(i,j)$满足四边形不等式,即$w(i,j)+w(i+1,j+1)\leq w(i,j+1)+w(i+1,j)$

通过简单的求和即可证明,上述不等式恒取等号

2. 证明$dp(i,j)$满足四边形不等式,即$dp(i,j)+dp(i+1,j+1)\leq dp(i,j+1)+dp(i+1,j)$

这个我实在没证出来...听说可能可以用归纳法

3. 由$w(i,j),dp(i,j)$满足四边形不等式,推出$dp(i,j)$满足决策单调性

若记$dp(i,j)$由$s(i,j)=k$转移而来,$dp(i,j)$满足决策单调性就是指$s(i,j)$满足$s(i,j-1)\leq s(i,j)\leq s(i+1,j)$

先用反证法证明$s(i,j-1)\leq s(i,j)$

设$dp(i,j-1)$的最优转移为$x$,$dp(i,j)$的最优转移为$y$,且$y<x$,则有

\begin{align*}dp(i,j-1)&=dp_{k=x}(i,j-1)=dp(i,x)+dp(x+1,j-1)+w(i,j-1)\\ &\leq dp_{k=y}(i,j-1)=dp(i,y)+dp(y+1,j-1)+w(i,j-1)\end{align*}

\begin{align*}dp(i,j)&=dp_{k=y}(i,j)=dp(i,y)+dp(y+1,j)+w(i,j)\\ &\leq dp_{k=x}(i,j)=dp(i,x)+dp(x+1,j)+w(i,j)\end{align*}

由于$dp(i,j)$满足四边形不等式,于是有

\[dp(y+1,j-1)+dp(x+1,j)\leq dp(y+1,j)+dp(x+1,j-1)\]

对等式左右两边都加上$dp(i,x)+dp(i,y)+w(i,j-1)+w(i,j)$,得

\begin{align*}\text{左式}&=dp(i,y)+dp(y+1,j-1)+w(i,j-1)+dp(i,x)+dp(x+1,j)+w(i,j)\\ &=dp_{k=y}(i,j-1)+dp_{k=x}(i,j)\end{align*}

\begin{align*}\text{右式}&=dp(i,y)+dp(y+1,j)+w(i,j)+dp(i,x)+dp(x+1,j-1)+w(i,j-1)\\ &=dp_{k=y}(i,j)+dp_{k=x}(i,j-1)\end{align*}

于是有

\[dp_{k=y}(i,j-1)+dp_{k=x}(i,j)\leq dp_{k=y}(i,j)+dp_{k=x}(i,j-1)\]

在条件中,右式为$dp(i-1),dp(j)$的最优转移,应当比左式小,推出矛盾

于是,有$y\geq x$,即$s(i,j-1)\leq s(i,j)$

类似的,也可以用完全相同的方法反证得出$s(i,j)\leq s(i+1,j)$

(四边形不等式为$dp(i,y)+dp(i+1,x)\leq dp(i,x)+dp(i+1,y)$,左右两边都加上$dp(x+1,j)+dp(y+1,j)+w(i,j)+w(i+1,j)$)

 

由决策单调性,$s(i,j)$的大致情况就可以确定了

首先,$s(i,j)$是一个$n\times n$的上三角形

由于$s(i,j-1)\leq s(i,j)$,所以每一行从左到右都是单调不减的

又因为$s(i,j)\leq s(i+1,j)$,所以每一列从上到下也是单调不减的

 

然后考虑dp的顺序,由于$s(i,j-1)\leq s(i,j)\leq s(i+1,j)$,所以如果已知$s(i,j-1),s(i+1,j)$,那么$s(i,j)$的范围就会被限制住

所以dp的顺序应该是,外层$i$从大到小,内层$j$从小到大

 

这样一来可以计算整体的复杂度:

由于$s(i,j)$的范围被$s(i,j-1),s(i+1,j)$所限制,那么总的枚举次数就是$\sum_{i=1}^{n} \sum_{j=i}^{n} (s(i+1,j)-s(i,j-1))$,也就是时间复杂度

将括号内提出来,就是$\sum_{i=1}^{n} \sum_{j=i}^{n} s(i+1,j)-\sum_{i=1}^{n} \sum_{j=i}^{n} s(i,j-1)$,其中

\[\sum_{i=1}^{n}\sum_{j=1}^{n}s(i+1,j)\leq \sum_{i=2}^{n}\sum_{j=i}^{n}s(i,j)+2n^2\]

\[\sum_{i=1}^{n}\sum_{j=1}^{n}s(i,j-1)\geq \sum_{i=1}^{n-1}\sum_{j=i}^{n-1}s(i,j)\]

于是就能得到

\begin{align*}&\sum_{i=1}^{n} \sum_{j=i}^{n} s(i+1,j)-\sum_{i=1}^{n} \sum_{j=i}^{n} s(i,j-1)\\ \leq &\sum_{i=2}^{n}\sum_{j=i}^{n}s(i,j)+2n^2-\sum_{i=1}^{n-1}\sum_{j=i}^{n-1}s(i,j)\\ \leq &\sum_{i=1}^{n} s(i,n)+2n^2\\ \leq &\ 3n^2\end{align*}

所以这种方法的时间复杂度为$O(n^2)$

 

这里就不写环状的了,反正只是把$a_i$再复制一份而已

决策单调性优化DP
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N=1005;
const int INF=1<<30;

int n;
int a[N],pre[N];

int s[N][N];
int dp[N][N];

int main()
{
//    freopen("input.txt","r",stdin);
//    freopen("my.txt","w",stdout);
    
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]),pre[i]=pre[i-1]+a[i];
    
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            dp[i][j]=INF;
    for(int i=1;i<=n;i++)
        s[i][i]=i,dp[i][i]=0;
    
    for(int i=n-1;i>=1;i--)
        for(int j=i+1;j<=n;j++)
            for(int k=s[i][j-1];k<=s[i+1][j];k++)
            {
                int val=dp[i][k]+dp[k+1][j]+pre[j]-pre[i-1];
                if(val<dp[i][j])
                {
                    s[i][j]=k;
                    dp[i][j]=val;
                }
            }
    
    printf("%d\n",dp[1][n]);
    return 0;
}
View Code

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-10-02
  • 2021-10-15
  • 2022-12-23
  • 2021-12-29
  • 2021-06-02
猜你喜欢
  • 2021-12-20
  • 2021-06-30
  • 2021-06-29
  • 2021-07-03
  • 2021-12-17
相关资源
相似解决方案