决策单调性优化dp

决策单调性:

对于一些dp方程,经过一系列的猜想和证明,可以得出,所有取的最优解的转移点(即决策点)位置是单调递增的。

即:假设f[i]=min(f[j]+b[j]) (j<i) 并且,对于任意f[i]的决策点g[i],总有f[i+1]的决策点g[i+1]>=g[i](或者<=g[i]) 那么,这个方程就具备决策单调性。

这个有什么用吗?

不懂具体优化方法的话确实也没有什么用。可能还是n^2的。只不过范围可能少了一些。

经典入门例题:

Description:

[POI2011]Lightning Conductor

已知一个长度为n的序列a1,a2,...,an。

对于每个1<=i<=n,找到最小的非负整数p满足 对于任意的j, aj < = ai + p - sqrt(abs(i-j))

Solution:

题目转化一下:就是对于每个i,找到aj+sqrt(abs(i-j))的最大值。

首先必须要先证明决策单调性:

当i>j时,即aj+sqrt(i-j)<=ai

假设对于i位置的决策点为g[i],那么对于任意的正数k,满足a[g[i]-k] + sqrt(i-g[i]+k) <= a[g[i]] + sqrt(i-g[i])

当i变成i+1 的时候, 因为幂函y = sqrt(x)是下凸的,

因为i-g[i]+k < i-g[i]

所以,sqrt(i+1-g[i]+k)-sqrt(i-g[i]+k) <  sqrt(i+1-g[i]) - sqrt(i-g[i]) (越大,加1越不明显)

大概就是,A区域一段的增加幅度大于B区域一段。

所以:a[g[i]-k] + sqrt(i+1-g[i]+k) < a[g[i]] + sqrt(i+1-g[i])  不选的那个点更不优了。g[i+1]>=g[i] 所以有单调性。

当j>i 的时候,同理可证。

处理方法:

1.分治:

sol(l,r,L,R) 表示决策l,r这段区间,决策点可能在L,R。

每次取一个mid=(l+r)/2 , 暴力扫描L,R找到决策点id。分治到:sol(l,mid-1,L,id) 和 sol(mid+1,r, id,R)

因为对于>mid 的点,决策一定比id大。反过来同理。

logn层,每层扫描总共O(n) 复杂度nlogn

这个题用这个方法代码好写。

注意理解一下i<j和i>j的情况两次找。

#include<bits/stdc++.h>
using namespace std;
const int N=500000+10;
double f[N][2];
int a[N],n;
void sol(int l,int r,int L,int R,int ty){
    if(l>r) return;
    int mid=(l+r)>>1;int id=0;
    for(int i=L;i<=min(R,mid);i++){//注意,这里min(R,mid),每次只从前部分取max
        //保证了i<j或者i>j的前提下的决策单调性。(虽然看似都是j<i的单调性,但是我对称数组了呀)
        //可以根据题目样例画图理解一下
        if(1.0*a[i]+1.0*sqrt(1.0*abs(i-mid))>f[mid][ty]){
            f[mid][ty]=1.0*a[i]+1.0*sqrt(1.0*abs(i-mid));
            id=i;
        }
    }
    sol(l,mid-1,L,id,ty);sol(mid+1,r,id,R,ty);
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    sol(1,n,1,n,1);
    for(int i=1;i<=n/2;i++) swap(a[i],a[n-i+1]);//这里要对称一下,正反分别找一遍,
    //对应i<j和i>j的情况,这样决策才是一直单调的。
    sol(1,n,1,n,0);
    for(int i=1;i<=n;i++){
        int ans=(int)ceil(f[i][1])-a[n-i+1];
        int aa=(int)ceil(f[n-i+1][0])-a[n-i+1];
        ans=max(ans,aa);
        printf("%d\n",ans);
    }
    return 0;
}

缺陷在于:这个方法必须依赖于f[i]与之前的f们无关。因为先得出答案的是区间中间的值,没有任何顺序。

一般适用于二维dp —— SD_le

2.队列:

这个方法适用性比较普遍。

我们把思路转化一下,从考虑每个位置的决策点,到每个点是哪些位置的决策点。

根据决策单调性,这些位置一定是连续的一段区间。

从前到后1~n枚举i

每次枚举到i,就从队头取出最优解,更新答案。再用二分查找决策分界点。插入队尾。

具体解释:

维护一个队列,队列里存着三元组(id,l,r),表示决策点和以id为最优决策点的区间(l,r)i从1扫到n
①i的最优决策点一定是队头的id,然后如果队头的r==i,弹出队头
②以i为最优决策点的区间一定是(x,n],然后从队列尾开始,如果队尾的q[tail].id在q[tail].l~q[tail].r中全劣于i,弹出队尾。
③当前队尾的元素在l~r中可能前一部分优于i,后一部分劣于i,二分这个分界点,修改q[tail].r
④如果q[tail].r!=n,将(i,q[tail].r+1,n)加入队尾
概括地说,1.取队头,2.判断弹队头 3.新决策点二分一个影响区间 4. 弹出队尾,或者弹出自己或者改变队尾区间影响范围。(5.加入自己)
不断循环3、4步,直到弹出自己或者改变范围。

循环当前的i,二分,nlogn

对于这个题,就是每次的i,找队头的id,更新f[i],把自己加进去。

因为每次决策点都是之前的,所以也要对称数组再做一遍即可。

决策单调性感觉非常微妙,是的,微妙。

必须要往这方面去想。一般要通过推式子。(打表找规律)



证明单调性后,就比较套路了。

原文地址:https://www.cnblogs.com/Miracevin/p/9348075.html

时间: 2024-08-27 17:56:17

决策单调性优化dp的相关文章

决策单调性优化dp 专题练习

决策单调性优化dp 专题练习 优化方法总结 一.斜率优化 对于形如 \(dp[i]=dp[j]+(i-j)*(i-j)\)类型的转移方程,维护一个上凸包或者下凸包,找到切点快速求解 技法: 1.单调队列 : 在保证插入和查询的x坐标均具有单调性时可以使用 2.单调栈+二分:保证插入有单调性,不保证查询有单调性 3.分治+ 1 或 2:在每次分治时将\([l,mid]\)这段区间排序后插入,然后更新右区间\([mid+1,r]\)的答案 二.分治.单调队列维护有单调性的转移 (甚至还有分治套分治)

题解——[NOI2009]诗人小G 决策单调性优化DP

第一次写这种二分来优化决策单调性的问题.... 调了好久,,,各种细节问题 显然有DP方程: f[i]=min(f[j] + qpow(abs(sum[i] - sum[j] - L - 1))); 其中f[i]代表到了第i个句子的最小答案 qpow用于处理^p sum为前缀和 (同时为了处理句子之间的空格问题,我们在统计前缀和的时候就默认在句子后面加一个空格, 然后在计算的时候,由于每一行只有最后一个不用加空格,直接减掉这个多加的空格即可获得正确长度) 首先我们可以打表发现是满足决策单调性的,

Codeforces 868F. Yet Another Minimization Problem【决策单调性优化DP】【分治】【莫队】

LINK 题目大意 给你一个序列分成k段 每一段的代价是满足\((a_i=a_j)\)的无序数对\((i,j)\)的个数 求最小的代价 思路 首先有一个暴力dp的思路是\(dp_{i,k}=min(dp_{j,k}+calc(j+1,i))\) 然后看看怎么优化 证明一下这个DP的决策单调性: trz说可以冥想一下是对的就可以 所以我就不证了 (其实就是决策点向左移动一定不会更优) 然后就分治记录当前的处理区间和决策区间就可以啦 //Author: dream_maker #include<bi

[BZOJ4709][JSOI2011]柠檬 决策单调性优化dp

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4709 我好弱啊QAQ,网上dalao们的题解根本看不懂啊,折腾了几个小时,有一点明白了. 首先要把朴素dp方程退出来. ①题目中说每次从序列的左右选一端取,但是如果你真的照着题目说的这样做我也不知道会怎么样.事实上很明显不管怎么取,最终答案都只跟划分出的是哪几个区间有关.所以不妨从左端开始取. ②如果取一个区间,区间第一个贝壳的大小和最后一个贝壳的大小不一样,那么很明显可以去掉第一个或最

P3515 [POI2011]Lightning Conductor[决策单调性优化]

给定一序列,求对于每一个$a_i$的最小非负整数$p_i$,使得$\forall j \neq i $有$ p_i>=a_j-a_i+ \sqrt{|i-j|}$. 绝对值很烦 ,先分左右情况单独做.现在假设j都在i左边,则$p_i=max{a_j-a_i+ \sqrt{i-j}}=max{a_j+ \sqrt{i-j} }-a_i$.带根号,不易斜率优化,考虑证决策单调性. 假设最优决策为j,j之前的任意决策称之为$j'$,则有 $f[j]+\sqrt{i-j} \geqslant f[j']

Bzoj 1563: [NOI2009]诗人小G(决策单调性优化)

原题面 带有详细证明的转这里 题意:每一个线段有一个长度,有一个标准长,现在要把这些线段按照顺序分行,每行的不和谐值等于标准长和该行线段总长的差的绝对值的p次方.现在要求最小的不和谐值之和. 开始的时候完全读错题了,以为p==2 for ever.真是太天真.后来看数据范围才发现.我真是面向数据编程? n^2的dp是一眼秒的.然后如果是p=2,那就是一个非常像玩具装箱的斜率优化dp.对于4.5的数据范围.可以想到用贪心优化dp.因为每一行的长度不会通过拼接线段(线段条数>=2)达到2*标准长,这

bzoj2216: [Poi2011]Lightning Conductor(分治决策单调性优化)

每个pi要求 这个只需要正反DP(?)一次就行了,可以发现这个是有决策单调性的,用分治优化 #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> #include<algorithm> using namespace std; const int maxn=500010,inf=1e9; int n; int a[

CodeForces 868F Yet Another Minimization Problem(决策单调性优化 + 分治)

题意 给定一个序列 \(\{a_1, a_2, \cdots, a_n\}\),要把它分成恰好 \(k\) 个连续子序列. 每个连续子序列的费用是其中相同元素的对数,求所有划分中的费用之和的最小值. \(2 \le n \le 10^5, 2 \le k \le \min(n, 20), 1 \le a_i \le n\) 题解 \(k\) 比较小,可以先考虑一个暴力 \(dp\) . 令 \(dp_{k, i}\) 为前 \(i\) 个数划分成 \(k\) 段所需要的最小花费. 那么转移如下

Gym - 101981B Tournament (WQS二分+单调性优化dp)

题意:x轴上有n个人,让你放置m个集合点,使得每个人往离他最近的集合点走,所有人走的距离和最短. 把距离视为花费,设$dp[i][k]$表示前i个人分成k段的最小花费,则有递推式$dp[i][k]=min\{dp[j][k-1]+w(j,i)\}$,其中$w(j,i)$可以$O(1)$求出. 显然,如果考虑段数的话,光状态数就有n^2个,肯定行不通.不过这题的最优解对段数的函数是凸的,因此可以用WQS二分来打破段数的限制. 给每个集合点加上一个额外的花费c,然后忽略段数的限制,这样递推式就变成了