四边形优化 dp


前言

对于经典区间dp问题,例如石子合并,有以下转移方程:

复杂度 n 3 n^3 n3,可以用四边形优化到 n 2 n^2 n2,GarsiaWachs算法可以做到 n l o g n nlogn nlogn


一、特殊性质

对于上式,可以根据 w ( l , r ) w(l,r) w(l,r)的一些特殊性质,利用决策单调性进行优化

  • 区间包含单调性:对于任意 l ≤ l ′ ≤ r ′ ≤ r l\le l'\le r'\le r llrr,均有 w ( l ′ , r ′ ) ≤ w ( l , r ) w(l',r')\le w(l,r) w(l,r)w(l,r)成立,则称函数 w w w对于区间包含关系具有单调性。
  • 四边形不等式:如果对于任意 l 1 ≤ l 2 ≤ r 1 ≤ r 2 l_1\le l_2\le r_1\le r_2 l1l2r1r2,均有 w ( l 1 , r 1 ) + w ( l 2 , r 2 ) ≤ w ( l 1 , r 2 ) + w ( l 2 , r 1 ) w(l_1,r_1)+w(l_2,r_2)\le w(l_1,r_2)+w(l_2,r_1) w(l1,r1)+w(l2,r2)w(l1,r2)+w(l2,r1)成立,则称函数 w w w满足四边形不等式(简记为“交叉小于包含”)。若等号永远成立,则称函数 w w w满足四边形恒等式

二、定理、引理

引理1

w ( l , r ) w(l,r) w(l,r)满足区间包含单调性和四边形不等式,则状态 f l , r f_{l,r} fl,r满足四边形不等式。

定理1

若状态 f f f满足四边形不等式,记 m l , r = m i n { k : f l , r = g k , l , r } m_{l,r}=min\{k:f_{l,r}=g_{k,l,r}\} ml,r=min{k:fl,r=gk,l,r}表示最优决策点,则有
m l , r − 1 ≤ m l , r ≤ m l + 1 , r m_{l,r-1}\le m_{l,r}\le m_{l+1,r} ml,r1ml,rml+1,r 其中 ( l + 1 < r ) (l+1<r) (l+1<r)
这是优化的关键
证明

核心代码

// C++ Version
for (int len = 2; len <= n; ++len)  // 枚举区间长度
  for (int l = 1, r = len; r <= n; ++l, ++r) {
    // 枚举长度为len的所有区间
    f[l][r] = INF;
    for (int k = m[l][r - 1]; k <= m[l + 1][r]; ++k)
      if (f[l][r] > f[l][k] + f[k + 1][r] + w(l, r)) {
        f[l][r] = f[l][k] + f[k + 1][r] + w(l, r);  // 更新状态值
        m[l][r] = k;  // 更新(最小)最优决策点
      }
  }

例题

石子合并

pass

邮局

题意

数轴上V个村庄,要建P个邮局,使得每个村庄到最近的邮局之间距离之和最小

思路

首先将坐标排序。

定义 d p [ i ] [ j ] dp[i][j] dp[i][j]表示前 i i i个村庄放 j j j个邮局的前 i i i个村庄的最小距离总和, w ( i , j ) w(i,j) w(i,j)表示村庄区间 [ i , j ] [i,j] [i,j]内放一个村庄时该区间的最小距离总和。
易得 d p [ i ] [ j ] = m i n { d p [ k ] [ j − 1 ] + w ( k + 1 , j ) } dp[i][j]=min\{dp[k][j-1]+w(k+1,j)\} dp[i][j]=min{dp[k][j1]+w(k+1,j)},其中 k ∈ [ 0 , i ) k\in [0,i) k[0,i)

n 2 n^2 n2预处理 w w w
显然,村庄数为奇数,邮局放中间的村庄,村庄数为偶数,放中间两个村庄之间(包含端点)
则有: w [ l ] [ r ] = w [ l ] [ r − 1 ] + X [ r ] − X [ l + r 2 ] w[l][r]=w[l][r-1]+X[r]-X[\frac{l+r}{2}] w[l][r]=w[l][r1]+X[r]X[2l+r]
l e n = l + r − 1 len=l+r-1 len=l+r1为奇数,则 l e n − 1 len-1 len1为偶数,偶数的时候取的是中间两个点都可以,奇数就是中间一个点,所以原本是取中间偏左的点,现在取偏右的点,对区间 [ l , r − 1 ] [l,r-1] [l,r1]不影响,只需加上右端点的贡献
l e n len len为偶数,则 l e n − 1 len-1 len1为奇数,则原先取中间点,现在取中间偏左的点,是一个点,所以对区间 [ l , r − 1 ] [l,r-1] [l,r1]也不影响,只需加上右端点的贡献
此时,复杂度为 O ( n 3 ) O(n^3) O(n3)
四边形优化
w w w是满足四边形不等式的 证明
因此根据上面的定理, f f f也是满足四边形不等式,最优决策记为 m [ i ] [ j ] m[i][j] m[i][j],则 m [ i ] [ j − 1 ] ≤ m [ i ] [ j ] ≤ m [ i + 1 ] [ j ] m[i][j-1]\le m[i][j]\le m[i+1][j] m[i][j1]m[i][j]m[i+1][j]
f [ i ] [ j ] f[i][j] f[i][j]时,在 [ m [ i ] [ j − 1 ] , m [ i + 1 ] [ j ] ] [m[i][j-1],m[i+1][j]] [m[i][j1],m[i+1][j]]中找最优解
由于要用到 m [ i + 1 ] [ j ] m[i+1][j] m[i+1][j],所以倒序求解。
时间复杂度 O ( P V ) O(PV) O(PV)

代码

#include <bits/stdc++.h>
#pragma optimize GCC(3)
#define show(x) cerr<<#x<<" : "<<x<<endl;
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 3e3 + 5;
int x[maxn];
int w[maxn][maxn];
int f[maxn][maxn], m[maxn][maxn];
void solve() {
    int V, P; cin >> V >> P;
    for (int i = 1; i <= V; i++)cin >> x[i];
    sort(x + 1, x + V + 1);
    //预处理w
    for (int i = 1; i <= V; i++)
        for (int j = i + 1; j <= V; j++)
            w[i][j] = w[i][j - 1] + x[j] - x[(i + j) / 2];
    
    memset(f, inf, sizeof(f));
    f[0][0] = 0;
    for (int j = 1; j <= P; j++) {
        m[V + 1][j] = V;
        for (int i = V; i >= 1; i--) {
            for (int k = m[i][j - 1]; k <= m[i + 1][j]; k++) {
                if (f[i][j] > f[k][j - 1] + w[k + 1][i]) {
                    f[i][j] = f[k][j - 1] + w[k + 1][i];
                    m[i][j] = k;
                }
            }
        }
    }
    cout << f[V][P];
}
signed main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    //int t; cin >> t; while (t--)
    solve();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

General.song

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值