决策单调性优化DP+分治优化决策单调性

大兔子大兔子 提交于 2019-12-07 17:53:27

前言

本来这篇已经写了\(\frac{2}{3}\)了 然后我关机时忘保存了。。。


华丽的分割线

决策单调性优化\(DP\)

对于类似于

\[dp[i][j]=max/min(dp[k - 1][j - 1] + count(k,i))\]

不妨设 当 最后一次 \(max/min\)更新时

\[f(i,j)=k\]

若有

\[\forall i,j\in[1,n],s.t. i < j \Rightarrow f(i,k)<=f(j,k)\]

我就可以称她具有决策单调性

例题

[HNOI2008]玩具装箱TOY

题意我就不概括了

据题 容易推出状态方程

\[dp[i]=min(dp[j-1]+count(i,j))\]

凭感觉 是具有决策单调性的

其实可以证明

不过我太菜了 不会

既然决策具有单调性

那么对于每一个决策点 我们可以拿出一个决策区间

用一个双端队列维护 决策点 和 决策区间

在每一次循环前

把区间\(.r<i\)的舍去

然后再以当前点为决策点看是否能比队列中的对后面的贡献更小

while(l <= r&&dp[i] + count(q[r].l,i) <= dp[q[r].pos] + count(q[r].l,q[r].pos)) r--;

注意\(while\)结束后还要特判一下

\[dp[i] + count(q[r].l,i) <= dp[q[r].pos] + count(q[r].l,q[r].pos)\]

不见得

\(\forall x \in[q[r].l,q[r].r]\Rightarrow dp[i] + count(x,i) > dp[q[r].pos] + count(x,q[r].pos)\)

所以还要二分找一下那个特殊的位置

因为决策点具有单调性\(\Rightarrow\)决策区间具有单调性\(\Rightarrow\)维护的双端队列具有单调性

与单调队列类似 每点只进入和删除一次

但是有二分

时间复杂度\(O(nlogn)\)

\(Code\)

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define reg register int
#define isdigit(x) ('0' <= x&&x <= '9')
template<typename T>
inline T Read(T Type)
{
    T x = 0,f = 1;
    char a = getchar();
    while(!isdigit(a)) {if(a == '-') f = -1;a = getchar();}
    while(isdigit(a)) {x = (x << 1) + (x << 3) + (a ^ '0');a = getchar();}
    return x * f;
}
typedef long long ll;
const int MAXN = 50010;
int n,L,a[MAXN];
ll dp[MAXN],sum[MAXN];
struct node
{
    int pos,l,r;
    void ass(int Pos,int L,int R) {pos = Pos,l = L,r = R;}
}q[MAXN];
inline ll co(ll x) {return x * x;}
inline ll count(int i,int j) {return co(sum[i] - sum[j] + i - j - 1 - L);}
inline int get_(int x,node seq)
{
    int l = seq.l,r = seq.r;
    while(l <= r)
    {
        int mid = l + r >> 1;
        if(dp[x] + count(mid,x) <= dp[seq.pos] + count(mid,seq.pos))
        {
            if(r == mid) return r;
            r = mid - 1;
        }
        else l = mid + 1;
    }
    return l;
}
int main()
{   n = Read(1),L = Read(1);
    for(reg i = 1;i <= n;i++) sum[i] = (a[i] = Read(1)) + sum[i - 1];
    memset(dp,0x7f7f7f,sizeof(dp));
    dp[0] = 0;
    int l = 1,r = 0;
    q[++r].ass(0,1,n);
    for(reg i = 1;i <= n;i++)
    {
        while(q[l].r < i) l++;
        dp[i] = dp[q[l].pos] + count(i,q[l].pos);
        q[l].l = i + 1;
        while(l <= r&&dp[i] + count(q[r].l,i) <= dp[q[r].pos] + count(q[r].l,q[r].pos)) r--;
        int pos = get_(i,q[r]);
        q[r].r = pos - 1;
        if(pos <= n) q[++r].ass(i,pos,n);
    }
    printf("%lld\n",dp[n]);
    return 0;
}

这类\(DP\) 通常还能再优化

也许是用斜率 或单调队列

但是决策单调性的好想,好实现及其优于暴力的特点让我们常常使用

分治优化决策单调性

因为 决策具有单调性

那么就可以使用分治优化

CF321E Ciel and Gondolas

#include <cstdio>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
#define reg register int
#define isdigit(x) ('0' <= x&&x <= '9')
template<typename T>
inline T Read(T Type)
{
    T x = 0,f = 1;
    char a = getchar();
    while(!isdigit(a)) {if(a == '-') f = -1;a = getchar();}
    while(isdigit(a)) {x = (x << 1) + (x << 3) + (a ^ '0');a = getchar();}
    return x * f;
}
const int MAXN = 4010,inf = 1000000000;
int sum[MAXN][MAXN],dp[MAXN][810];
inline int count(int i,int j) 
{return sum[i][i] - sum[i][j - 1] - sum[j - 1][i] + sum[j - 1][j - 1];}
inline void dfs(int k,int l,int r,int opl,int opr)
{
    if(l > r) return;
    int mid = l + r >> 1;
    int minl = inf,id;
    for(int i = opl;i <= min(opr,mid);i++)
    {
        int cur = dp[i - 1][k - 1] + count(mid,i);
        if(cur < minl) minl = cur,id = i;
    }
    dp[mid][k] = minl;
    dfs(k,l,mid - 1,opl,id);
    dfs(k,mid + 1,r,id,opr);
}
int main()
{
    int n = Read(1),k = Read(1);
    for(reg i = 1;i <= n;i++)
        for(reg j = 1;j <= n;j++)
        {
             int v = Read(1);
             sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + v;
        }
    for(reg i = 1;i <= n;i++) dp[i][0] = inf;
    for(reg i = 1;i <= k;i++) dfs(i,1,n,1,n);
    printf("%d",dp[n][k] / 2);
    return 0;   
} 
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!