首页 > 其他分享 >决策单调性优化DP

决策单调性优化DP

时间:2024-05-22 17:40:19浏览次数:27  
标签:lf le int mid sqrt DP 优化 单调

@

目录
同步发表于CSDN

决策单调性

四边形不等式

定义:
对于二元函数 \(w(x,y)\),若 \(\forall a,b,c,d\in \mathbb{Z}\),且 \(a\le b\le c\le d\),都有 \(w(a,d)+w(b,c)\ge w(a,c)+w(b,d)\),则称函数 \(w\) 满足四边形不等式,也称它满足凸完全单调性

简记为 跨越 \(+\) 包含 \(\ge\) 交叉

定理1:
若函数 \(w(x,y)\) 满足 \(\forall a,b\in \mathbb{Z},a<b\) 都有 \(w(a,b+1)+w(a+1,b)\ge w(a,b)+w(a+1,b+1)\)
则函数 \(w\) 满足四边形不等式

证明1:
若\(a<c\),有 \(w(a,c+1)+w(a+1,c)\ge w(a,c)+w(a+1,c+1)\)
若\(a+1<c\),有 \(w(a+1,c+1)+w(a+2,c)\ge w(a+1,c)+w(a+2,c+1)\)
两式相加,得 \(w(a,c+1)+w(a+1,c)+w(a+1,c+1)+w(a+2,c)\ge w(a,c)+w(a+1,c+1)+w(a+1,c)+w(a+2,c+1)\)
即 \(w(a,c+1)+w(a+2,c)\ge w(a,c)+w(a+2,c+1)\)
同理可证,\(\forall a\le b\le c,w(a,c+1)+w(b,c)\ge w(a,c),w(b,c+1)\)
同理可证,\(\forall a\le b\le c\le d\),\(w(a,d)+w(b,c)\ge w(a,c)+w(b,d)\)

决策单调性

定义:
考虑转移方程 \(f[i]=\min_{0\le j <i} (f[j]+w(j,i))\)
令 \(p[i]\) 表示 \(i\) 的最优决策 \(j\),即让 \(f[i]\) 取最小值的 最小 \(j\)
若 \(p[i]\) 在 \([1,n]\) 上单调不减,则称 \(f\) 具有决策单调性

定理2:
考虑转移方程 \(f[i]=\min_{0\le j <i} (f[j]+w(j,i))\),若函数 \(w\) 满足四边形不等式,则 \(f\) 具有决策单调性
证明2:
\(\forall i\in [1,n],j\in[0,p[i]-1],f[p[i]]+w(p[i],i)\le f[j]+w(j,i)\) \(\color{red}(1)\)
\(\forall i'\in [i+1,n],j<p[i]<i<i',w(j,i')+w(p[i],i)\ge w(j,i)+w(p[i],i')\)
\(\therefore w(p[i],i')-w(p[i],i)\le w(j,i')-w(j,i)\) \(\color{red}(2)\)
\(\color{red}(1)\)+\(\color{red}(2)\) 得: \(f[p[i]]+w(p[i],i')\le f[j]+w(j,i')\)
\(\color{red} \therefore p[i']\ge p[i]\)
所以 \(f\) 具有决策单调性

形式1

用于优化形如 \(f[i]=\min_{1\le j\le i} w(j,i)\) 的转移方程。
因为我们只需要找到每一个 \(p[i]\),我们就可以算出每一个 \(f[i]\) 了,那么对于这种方法,我们有两种常见方法。

法1 分治

考虑求区间 \([1,n]\) 的 \(p[i]\),我们先求出 \(p[mid]\),再把它分成两个区间 \([1,mid-1]\) 和 \([mid+1,n]\) 分治求解。
为什么可以呢,因为根据决策单调性,\(\forall i\in[mid+1,n],p[i]\ge p[mid],\forall j\in [1,mid-1],p[j]\le p[mid]\)
所以可以分治来求答案。
参考代码:

void solve(int l,int r,int pl,int pr){
	// 求pmid=p[mid]
	//算出 f[mid]
	solve(l,mid-1,p,pmid);
	solve(mid+1,r,pmid,pr);
}

法2 二分队列

易证,每一个决策一定是在一个区间内的,例如:

\[p[i](i\in[1,4])=1,p[i](i\in[5,9])=3,p[i](i \in[10,n])=k \]

所以可以维护一个单调队列,用于表示每个决策 \(i\) 所对应的最优转移点。
参考代码:

void solve(){
	h=1,t=0;
	for(int i=1;i<=n;i++){
		insert(i);
		if(h<=t&&q[h].r<i)
			h++;
		else
			q[h].l=i;
		f[i]=min(f[i],w(q[h].p,i));
	}
}

例题 P3515

给定一个长度为 \(n\) 的序列 \(\{a_n\}\),对于每个 \(i\in [1,n]\) ,求出一个最小的非负整数 \(p\) ,使得 \(\forall j\in[1,n]\),都有 \(a_j\le a_i+p-\sqrt{|i-j|}\)

\(1 \le n \le 5\times 10^{5}\),\(0 \le a_i \le 10^{9}\) 。

Solution

先变形:
\(p\ge a[j]+\sqrt{|i-j|}-a[i],\forall j\in[1,n]\)

问题变为求 \(\max(a[j]+\sqrt{|i-j|}),\forall j\in[1,n]\)

考虑将序列先算一遍,翻转一次后再算一遍,最后求最大值,即变为求 \(\max(a[j]+\sqrt{|i-j|}),\forall j\in[1,i]\)

令 \(f[i]=\max(a[j]+\sqrt{i-j})\)
此处 \(w(j,i)=\sqrt{i-j}\)
定义 \(a+1<c\)
则有:
\(w(a,c)=\sqrt{c-a},w(a+1,c)=\sqrt{c-a-1},w(a,c+1)=\sqrt{c-a+1},w(a+1,c+1)=\sqrt{c-a}\)

\(\therefore w(a,c+1)+w(a+1,c)-w(a,c)-w(a+1,c+1)=\sqrt{c-a-1}+\sqrt{c-a+1}-2\sqrt{c-a}\)
令 \(d=c-a\)
原式 \(=(\sqrt{d+1}-\sqrt{d})-(\sqrt{d}-\sqrt{d-1})\)
对函数 \(f(x)=\sqrt{x}-\sqrt{x-1}\) 求导发现其单调递减,所以原式恒小于 \(0\),即可得:

\[w(a,c+1)+w(a+1,c)\le w(a,c)+w(a+1,c+1) \]

可以发现,它跟四边形不等式符号相反,同样亦可证得 \(f\) 满足决策单调性,于是可以套用 法\(1\) 和法 \(2\) 进行求解。
参考代码:
F1:

#include<bits/stdc++.h>
#define int __int128
#define lf double
using namespace std;
const int N=5e5+1,mod=1e9+7;
const lf eps=1e-5;
int n,a[N];
lf sq[N],f[N];
inline lf w(int j,int i){
	return 1.0*a[j]+sq[i-j];
}
inline int Ceil(lf x){
	return (int)(x+1-eps);
}
void solve(int l,int r,int pl,int pr){
	if(l>r)
		return ;
	int mid=(l+r)>>1,mxp;
	lf mx=0;
	for(int i=pl;i<=min(mid,pr);i++)
			if(w(i,mid)>mx)
				mx=w(i,mid),mxp=i;
	f[mid]=max(f[mid],mx);
	solve(l,mid-1,pl,mxp);
	solve(mid+1,r,mxp,pr);
}
signed main(){
	n=read();
	for(int i=1;i<=n;i++)
		a[i]=read(),sq[i]=sqrt((1.0*i));
	solve(1,n,1,n);
	for(int i=1;i<=n/2;i++)
		swap(a[i],a[n-i+1]),swap(f[i],f[n-i+1]);
	solve(1,n,1,n);
	for(int i=1;i<=n/2;i++)
		swap(a[i],a[n-i+1]),swap(f[i],f[n-i+1]);
	for(int i=1;i<=n;i++){
		write(Ceil(f[i]-a[i]));
		printf("\n");
	}
	return 0;
}

F2:

#include<bits/stdc++.h>
#define int __int128
#define lf double
using namespace std;
const int N=5e5+1,mod=1e9+7;
const lf eps=1e-5;
int n,a[N],h,t;
lf sq[N],f[N];
struct fy{
	int l,r,p;
}q[N];
inline lf w(int j,int i){
	return 1.0*a[j]+sq[i-j];
}
inline int Ceil(lf x){
	return (int)(x+1-eps);
}
int find_(int t,int x){
	int res=q[t].r+1,l=q[t].l,r=q[t].r,p=q[t].p;
	while(l<=r){
		int mid=(l+r)>>1;
		if(w(p,mid)<=w(x,mid))
			res=mid,r=mid-1;
		else
			l=mid+1;
	}
	return res;
}
void insert(int x){
	q[t].l=max(q[t].l,x);
	while(h<=t&&w(q[t].p,q[t].l)<=w(x,q[t].l))
		t--;
	if(h<=t){
		int mid=find_(t,x);
		if(mid>n)
			return ;
		q[t].r=mid-1;
		q[++t].l=mid,q[t].p=x,q[t].r=n;
	}
	else{
		q[++t].l=x,q[t].p=x,q[t].r=n;
	}
}
void solve(){
	h=1,t=0;
	for(int i=1;i<=n;i++){
		insert(i);
		if(h<=t&&q[h].r<i)
			h++;
		else
			q[h].l=i;
		f[i]=max(f[i],w(q[h].p,i));
	}
}
signed main(){
	n=read();
	for(int i=1;i<=n;i++)
		a[i]=read(),sq[i]=sqrt((1.0*i));
	solve();
	for(int i=1;i<=n/2;i++)
		swap(a[i],a[n-i+1]),swap(f[i],f[n-i+1]);
	solve();
	for(int i=1;i<=n/2;i++)
		swap(a[i],a[n-i+1]),swap(f[i],f[n-i+1]);
	for(int i=1;i<=n;i++){
		write(Ceil(f[i]-a[i]));
		printf("\n");
	}
	return 0;
}

形式2

用于优化形如 \(f[i]=\min_{0\le j<i}(f[j]+w(j+1,i))\) 的转移方程。(\(w\) 满足四边形不等式)
注意到此种转移方程依赖于前面的值,因此分治法不再适用,所以只能用二分队列,思路跟上面一摸一样。

例题 P3195

P 教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京。他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中。
P 教授有编号为 \(1 \cdots n\) 的 \(n\) 件玩具,第 \(i\) 件玩具经过压缩后的一维长度为 \(C_i\)。
为了方便整理,P教授要求:

  • 在一个一维容器中的玩具编号是连续的。
  • 同时如果一个一维容器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物。形式地说,如果将第 \(i\) 件玩具到第 \(j\) 个玩具放到一个容器中,那么容器的长度将为 \(x=j-i+\sum\limits_{k=i}^{j}C_k\)。
    制作容器的费用与容器的长度有关,根据教授研究,如果容器长度为 \(x\),其制作费用为 \((x-L)^2\)。其中 \(L\) 是一个常量。P 教授不关心容器的数目,他可以制作出任意长度的容器,甚至超过 \(L\)。但他希望所有容器的总费用最小。

对于全部的测试点,\(1 \leq n \leq 5 \times 10^4\),\(1 \leq L \leq 10^7\),\(1 \leq C_i \leq 10^7\)。

Solution

令 \(f[i]\) 表示前 \(i\) 个玩具装箱的最小代价,则枚举第 \(i\) 个玩具和那些玩具放在一个箱子中,可得转移方程:

\[f[i]=\min_{1\le j\le i}(f[j-1]+(i-j-L+\sum_{k=j}^iC_k)^2) \]

\(f\) 满足决策单调性,证明参考 OI-WIKI
image

补充:图片的 \(K\) 与题意中的 \(L\) 意思相同。
性质 \(1,2,3\):
image
那么把形式 \(1\) 例题的方法 \(2\) 的代码改装一下就可以了。
参考Code:

#include<bits/stdc++.h>
#define int __int128
#define lf double
using namespace std;
const int N=5e5+1,mod=1e9+7;
const lf eps=1e-5;
int n,a[N],h,t,L;
int f[N],sum[N];
struct fy{
	int l,r,p;
}q[N];
inline int w(int j,int i){
	return f[j-1]+(i-j+sum[i]-sum[j-1]-L)*(i-j+sum[i]-sum[j-1]-L);
}
int find_(int t,int x){
	int res=q[t].r+1,l=q[t].l,r=q[t].r,p=q[t].p;
	while(l<=r){
		int mid=(l+r)>>1;
		if(w(p,mid)>=w(x,mid))
			res=mid,r=mid-1;
		else
			l=mid+1;
	}
	return res;
}
void insert(int x){
	q[t].l=max(q[t].l,x);
	while(h<=t&&w(q[t].p,q[t].l)>=w(x,q[t].l))
		t--;
	if(h<=t){
		int mid=find_(t,x);
		if(mid>n)
			return ;
		q[t].r=mid-1;
		q[++t].l=mid,q[t].p=x,q[t].r=n;
	}
	else{
		q[++t].l=x,q[t].p=x,q[t].r=n;
	}
}
void solve(){
	h=1,t=0;
	for(int i=1;i<=n;i++){
		insert(i);
		if(h<=t&&q[h].r<i)
			h++;
		else
			q[h].l=i;
		f[i]=max(f[i],w(q[h].p,i));
	}
}
signed main(){
	n=read();
	L=read();
	for(int i=1;i<=n;i++)
		a[i]=read(),sum[i]=sum[i-1]+a[i];
	solve();
	write(f[n]);
	return 0;
}
//直接交不能AC哦
//而且此代码与OI-WIKI的略微有些不同

形式3

用于优化形如 \(f[k][i]=\min_{1\le j\le i}(f[k-1][j-1]+w(j,i))\) 的转移方程(\(w\) 满足四边形不等式)
注意到其实跟形式 \(1\) 没有什么区别,因为它不依赖于这一层的值,而是依赖于上一层的值,所以既可以分治也可以二分队列。
据作者喜好(不是作者太菜),例题中只给出分治做法。

例题 CF833B

将一个长度为 \(n\) 的序列分为 \(k\) 段,使得总价值最大。
一段区间的价值表示为区间内不同数字的个数。
\(n\leq 35000,k\leq 50\)

Solution

考虑决策单调性优化DP
令 \(f[i][j]\) 表示前 \(j\) 个数分成 \(i\) 段的最小费用,则可得:

\[f[i][j]=\min_{1\le k<j}(f[i-1][k]+w(k+1,j)) \]

其中 \(w(l,r)\) 表示 下表为\([l,r]\) 中不同数字的个数。
证明 \(w\) 满足决策单调性:
手搓几个样例就可以了口糊过去
证明:
令 \(g(x,l,r)\) 表示 \(x\) 是否在区间 \([l,r]\) 内出现过,出现为 \(0\),否则为 \(1\),\(\Delta=[C_a==C_{b+1}]\)
\(w(a,b+1)+w(a+1,b)=2w(a+1,b)+g(C_a,a+1,b)+g(C_{b+1},a+1,b)-\Delta\)
\(w(a,b)+w(a+1,b+1)=2w(a+1,b)+g(C_a,a+1,b)+g(C_{b+1},a+1,b)\)
所以可得:
\(w(a,b+1)+w(a+1,b)\le w(a,b)+w(a+1,b+1)\)
因此 \(w\) 满足四边形不等式
所以 \(f\) 满足决策单调性

仅限于分治方法:
接下来就可以 Code了,不过维护 \(w(l,r)\) 时注意可以用莫队的思想,维护双指针,由于分治的特殊性可以保证时间复杂度为 \(O(n)\)
总时间复杂度:\(O(kn\log n)\)

参考Code:

#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
using namespace std;
const int N=1e5+1,mod=1e9+7;
int n,k,a[N],f[52][N],cnt[N],ans,l,r;
inline void add(int x){
	ans+=(++cnt[x]==1);
}
inline void del(int x){
	ans-=(--cnt[x]==0);
}
inline int w(int cl,int cr){
	while(l<cl)
		del(a[l++]);
	while(l>cl)
		add(a[--l]);
	while(r<cr)
		add(a[++r]);
	while(r>cr)
		del(a[r--]);
	return ans;
}
void solve(int l,int r,int pl,int pr,int now){
	if(l>r)
		return ;
	int mid=(l+r)>>1,mxp;
	int mx=0;
	for(int i=pl;i<=min(mid-1,pr);i++){
		int o=f[now-1][i]+w(i+1,mid);
		if(o>mx)
			mx=o,mxp=i;
	}
	f[now][mid]=max(mx,f[now-1][mid]);
	solve(l,mid-1,pl,mxp,now);
	solve(mid+1,r,mxp,pr,now);
}
signed main(){
	n=read(),k=read();
	for(int i=1;i<=n;i++)
		a[i]=read();
	l=1,r=0;
	ans=0;
	for(int i=1;i<=k;i++){
		solve(1,n,0,n,i);
	}
	cout<<f[k][n]<<"\n";
	return 0;
}
//注意不开快读会T哦

形式4

用于优化形如 \(f[i][j]=\min(f[i][k]+f[k+1][j]+w(i,j))\) 的区间DP转移方程
只需要一个简单的操作,就能把这个区间DP的时间复杂度从 \(O(n^3)\) 将为 \(O(n^2)\),就是把枚举 \(k\) 的代码从

for(int k=i;k<j;k++)

改为

for(int k=p[i][j-1];k<=p[i+1][j];k++)

其中 \(p[i][j]\) 表示 \(i\) ~ \(j\)的最优分割点。
证明可以参考 《算法竞赛》中的 5.10
和洛谷 石子合并 题解区中 Hurricane、 的题解。

例题

image

Solution

image
参考代码:

#include<bits/stdc++.h>
//#define int long long
#define IOS ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
using namespace std;
const int N=5e3+1,mod=1e9+7;
int n,a[N],s[N],f[N][N],p[N][N];
inline int read(){
	int x=0;
	char s=getchar();
	while(s<'0'||s>'9')
		s=getchar();
	while(s>='0'&&s<='9')
		x=x*10+s-'0',s=getchar();
	return x;
}
signed main(){
//	IOS;
	n=read();
	for(int i=1;i<=n;i++)
		a[i]=a[i+n]=read();
	for(int i=1;i<=2*n;i++)
		s[i]=s[i-1]+a[i],p[i][i]=i;
	for(int len=2;len<=n;len++)
		for(int l=1;l<=n*2-len+1;l++){
			int r=l+len-1;
			int res=1e9;
			for(int mid=p[l][r-1];mid<=p[l+1][r];mid++)
				if(f[l][mid-1]+f[mid+1][r]+s[r]-s[l-1]-a[mid]<res)
					res=f[l][mid-1]+f[mid+1][r]+s[r]-s[l-1]-a[mid],p[l][r]=mid;
			f[l][r]=res;
		}
	int ans=1e9;
	cout<<f[1][n];
	return 0;
}

后话

参考习题单
参考资料:

  1. OI-WIKI
  2. 决策单调性优化dp学习笔记
  3. 关于决策单调性优化动态规划

标签:lf,le,int,mid,sqrt,DP,优化,单调
From: https://www.cnblogs.com/conti123/p/18206788

相关文章

  • Linux之性能优化
    优化内核相关参数配置文件/etc/sysctl.conf配置方法直接将参数添加进文件每条一行sysctl-a可以查看默认配置sysctl-p执行并检测是否有错误网络相关net.core.somaxconn=65535一个端口最大监听TCP连接队列的长度net.core.netdev_max_backlog=65535数据包速率比内......
  • 如何提升百度小程序的收录?百度小程序如何做优化?
    ​ 如何通过百度小程序获得更多的自然流量?这是做百度小程序肯定要考虑的问题,做百度小程序的目的就是想借助百度生态,做相应的关键词给自己的小程序引流,如何把流量给做起来呢,接下来我从不同的方面给大家进行分析讲解。合理设置标题、关键词、页面描述​ 做web开发的时候我们都知道......
  • 三次握手和四次挥手、UDP、TCP、粘包问题、模块回顾
    【一】三次握手和四次挥手【1】TCP协议的三次握手和四次挥手TCP协议位于osi七层协议中的传输层(1)使用三次握手来建立连接一次握手:客户端发送带有SYN(SEQ=x)标志的数据包---》服务端,然后客户端进入SYN_SEND状态,等待服务器的确认。二次握手:服务端发送带有SYN+A......
  • H5 缓存机制浅析 移动端 Web 加载性能优化
     H5缓存机制浅析移动端Web加载性能优化 转自:https://www.cnblogs.com/bugly/p/5039153.html1H5缓存机制介绍H5,即HTML5,是新一代的HTML标准,加入很多新的特性。离线存储(也可称为缓存机制)是其中一个非常重要的特性。H5引入的离线存储,这意味着web应用可......
  • m基于BP译码算法的LDPC编译码matlab误码率仿真,对比不同的码率
    1.算法仿真效果matlab2022a仿真结果如下:   2.算法涉及理论知识概要      低密度奇偶校验码(Low-DensityParity-CheckCode,LDPC码)是一种高效的前向纠错码,广泛应用于无线通信、数据存储等领域。BP(BeliefPropagation)译码算法,又称为消息传递算法,是LDPC码最常用......
  • 简单叙述MySQL如何优化?
    数据库设计优化:尽量减小占用磁盘空间:使用较小的数据类型,如mediumint替代int。定义字段为notnull,除非需要允许空值。对于不变的字段,如固定长度的字符串,采用char而不是varchar。优化索引设计:主索引应尽可能短,以提高效率。仅创建必要的索引,避免不必要的索引占用资源。......
  • EDP .Net开发框架--自动化日志
    平台下载地址:https://gitee.com/alwaysinsist/edp自动化日志不需要额外调用日志相关功能即可无感实现程序集方法调用的日志记录。创建业务逻辑处理类publicclassStudentBLL:BusinessLogicBase<StudentBLL>继承基类BusinessLogicBase<T>定义业务逻辑方法点击查看代......
  • 树形DP
    树形DP即在树上进行的DP。常见的两种转移方向:父节点\(\rightarrow\)子节点:如求节点深度,\(dp_u=dp_{fa}+1\)子节点\(\rightarrow\)父节点:如求子树大小,\(dp_u=1+\sumdp_v\)习题:P5658[CSP-S2019]括号树暴力本题\(n\)小的数据点保证为链,直接枚举\(i\),代......
  • Unity性能优化:什么是内存泄露?
    内存泄漏是优化方面的名词,主要是由于不再使用的资源没有及时清理,来释放内存,造成内存的浪费,造成系统卡顿。 或者说,内存就像花呗,额度就这么多,有借要有还,而且手里有闲钱的时候就记得还,以保证内存的充足,如果占着不用,就会在其他需要使用的时候内存不足,就容易崩溃出现问题。 Unity......
  • 美团一面:项目中有 10000 个 if else 如何优化?想了半天,被问懵了!
    大家好,我是R哥。最近做Java面试辅导,有个兄弟面试美团,遇到一个特别有意思的问题:一万个ifelse如何优化,有好的解决方案吗?我看到这问题都有点懵逼,现实项目中怎么可能会有10000个ifelse的代码,至少我工作10余年没见过样的代码。关键要写完这10000行的ifelse代码......