JS1k: Breathing Galaxies (1013 bytes)

DP 优化——决策单调性优化

决策单调性优化

决策单调性定义

对于 \(dp_i\),若 \(dp_i\)\(dp_j\) 转移最优,则 \(j\) 即为 \(dp_i\) 的最优决策点。

在状态转移方程 \(dp_i=min\{dp_j+w(j,i)\}^*\)\(j\in[0,i)\) 中,设 \(p_i\)\(dp_i\) 的最优决策点,若 \(p\)\([1,n]\) 上单调不减,则称 \(dp\) 具有决策单调性

\(*\)\(w(a,b)\) 是定义在整数集合上的二元函数,下同。

四边形不等式

定义

若函数 \(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(x,y)\) 满足四边形不等式。

推论

若函数 \(w(x,y)\) 满足对于 \(\forall a,b\in\mathbb Z\)\(a\le b\),都有 \(w(a+1,b)+w(a,b+1)\ge w(a,b)+w(a+1,b+1)\),则称函数 \(w(x,y)\) 满足四边形不等式。

证明

对于 \(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+2,c)\ge w(a,c)+w(a+2,c+1)\)

以此类推,对于任意的 \(a\le b\le c\),有 \(w(a,c+1)+w(b,c)\ge w(a,c)+w(b,c+1)\)

同理,对任意的\(a\le b\le c\le d\),有 \(w(a,d)+w(b,c)\ge w(a,c)+w(b,d)\)

决策单调性与四边形不等式

定理

在状态转移方程 \(dp_i=min\{dp_j+w(j,i)\}\)\(j\in[0,i)\) 中,若函数 \(w\) 满足四边形不等式,则 \(dp\) 具有决策单调性。

证明

定义 \(p_i\) 表示 \(i\) 的最优决策点。

\(\forall i\in[1,n],\forall j\in[0,p_i−1]\),根据 \(p_i\)\(i\) 的最优决策点,则有

\[dp_{p_i}+w(p_i,i)\le dp_j+w(j,i) \]

\(\forall i'\in[i+1,n]\),因为 \(w\) 满足四边形不等式,则有

\[w(j,i′)+w(p_i,i)\ge w(j,i)+w(p_i,i′) \]

移项,可得

\[w(p_i,i′)−w(p_i,i)\le w(j,i′)−w(j,i) \]

与第一个不等式相加,可得

\[dp_{p_i}+w(p_i,i′)≤dp_j+w(j,i′) \]

\(i′\) 的最优决策点一定大于等于 \(p_i\)。故 \(dp\) 具有决策单调性。

决策单调性优化

单调队列

当求出一个新的 \(dp_i\) 时,考虑 \(i\) 可以作为哪些 \(dp_i'\)\(i′>i\))的最优决策点。根据决策单调性,一定可以找到一个位置,在该位置之前,\(p\) 内存储的决策都比 \(i\) 要优,其后都比 \(i\) 差。于是便可以将该位置及其后面的部分全部变为 \(i\),即此时它们的最优决策均为 \(i\)

我们可以新建一个队列 \(q\)\(q\) 中每个元素是一个三元组 \(j,l,r\),表示 \(\forall k\)\(l\le k\le r\)),都有 \(p_k=j\)

对于每个 \(i\in[1,n]\),执行以下操作

  1. 检查队首:若队首为 \((j_{head},l_{head},r_{head})\),若 \(r_0<i\) 则删除队首。

  2. 根据队首的最优决策点 \(j\) 求出 \(dp_i\)

  3. 加入决策 \(i\)

    1. 取出队尾:\((j_{tail},l_{tail},r_{tail})\)
    2. 若对于 \(dp_{l_{tail}}\)\(i\) 是比 \(j_{tail}\) 更优的决策,即 \(dp_i+w(i,l_t)\le f_{j_{tail}}+w(j_t,l_t)\),记 \(pos=l_t\),删除队尾,返回步骤 1。
    3. 若对于 \(dp_{r_{tail}}\)\(i\) 是比 \(j_{tail}\) 更优的决策,即 \(f_{j_{tail}}+w(j_{tail},r_{tail})\le dp[i]+w(i,r_{tail})\),去往步骤 5。
    4. 否则,在 \([l_{tail},r_{tail}]\) 二分查找出位置 \(pos\),使得在 \(pos\) 之前的决策都比 \(i\) 优,在 \(pos\) 之后的决策都 \(i\) 更优,将 \([l_{tail},r_{tail}]\) 修改为 \([l_{tail},pos−1]\)
    5. 把三元组 \((i,pos,n)\) 插入队尾。

时间复杂度 \(O(n\log n)\)

分治

假设区间 \([l,r]\) 的最优决策点在区间 \([L,R]\),定义 \(mid=\frac{l+r}{2}\),设 \(dp_{mid}\) 的最优决策点是 \(pos\),由决策单调性可知区间 \([l,mid]\) 的最优决策点在区间 \(l,pos\),区间 \([mid+1,r]\) 的最优决策点在 \([pos,R]\)

问题分成了子问题,即可分治。

时间复杂度 \(O(n\log n)\)

P3515 [POI 2011] Lightning Conductor

题意:

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

由题意得:

\[a_j\le a_i+p−\sqrt{∣i−j∣},\forall j\in[1,n]\\ p\ge a_j+\sqrt{|i-j|}-a_i,\forall j\in[1,n]\]

所以:

\[p=\lceil\max\limits_{1\le j\le n}\{a_j+\sqrt{|i-j|}\}\rceil \]

我们可以做两次 \(dp\),第二次反转序列,可以消除一层绝对值,即 \(p=\lceil\max\limits_{1\le j\le n}\{a_j+\sqrt{i-j}\}\rceil\)

\(dp_i\) 表示 \(\lceil\max\limits_{1\le j\le n}\{a_j+\sqrt{i-j}\}\rceil\)\(w(j,i)\) 即为 \(\sqrt{i-j}\),接下来证明 \(w(j,i)\) 满足四边形不等式。

定义 \(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}\]

\[w(a+1,c)+w(a,c+1)-(w(a,b)+w(a+1,b+1))\\ =\sqrt{c-a+1}+\sqrt{c-a-1}-2\sqrt{c-a}\\ =(\sqrt{(c-a)+1}-\sqrt{c-a})-(\sqrt{c-a}-\sqrt{{c-a}-1})\]

因为 \(f(x)=\sqrt x-\sqrt{x-1}\) 单调递减,所以上式取值小于 \(0\),所以 \(w(j,i)\) 满足四边形不等式,\(dp\) 具有决策单调性。

代码

单调队列

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e5 + 5;
int n, a[N], head, tail;
double dp[N], sq[N];
struct node{
	int p, l, r;
}q[N];
double w(int j, int i){
	return (double)a[j] + sq[i - j];
}
int search(int p, int i){
	int l = q[p].l, r = q[p].r, ans = q[p].r + 1;
	int mid;
	while (l <= r){
		mid = l + r >> 1;
		if (w(i, mid) >= w(q[p].p, mid)){
			ans = mid, r = mid - 1;
		} else {
			l = mid + 1;
		}
	}
	return ans;
}
void insert(int i){
	q[tail].l = max(q[tail].l, i);
	while (head <= tail && w(i, q[tail].l) >= w(q[tail].p, q[tail].l))--tail;
	if (head > tail){
		q[++tail] = {i, i, n};
	} else {
		int pos = search(tail, i);
		if (pos > n)return;
		q[tail].r = pos - 1;
		q[++tail] = {i, pos, n};
	}
}
void solve(){
	head = 1, tail = 0;
	for (int i = 1; i <= n; ++i){
		insert(i);
		if (head <= tail && q[head].r < i)++head;
		else q[head].l = i;
		dp[i] = max(dp[i], w(q[head].p, i));
	}
}
signed main(){
	cin.tie(0)->sync_with_stdio(0);
	cin >> n;
	for (int i = 1; i <= n; ++i){
		cin >> a[i];
		sq[i] = sqrt(i);
	}
	solve();
	for (int i = 1; i <= (n >> 1); ++i){
		swap(a[i], a[n - i + 1]), swap(dp[i], dp[n - i + 1]);
	}
	solve();
	for (int i = n; i >= 1; --i){
		cout << (int)ceil(dp[i]) - a[i] << "\n";
	}
	return 0;
}

分治

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e5 + 5;
int n, a[N];
double dp[N], sq[N];
double w(int j, int i){
	return (double)a[j] + sq[i - j];
}
void solve(int l, int r, int L, int R){
	if (l > r)return;
	int mid = l + r >> 1, pos;
	double maxn = -1;
	for (int i = L; i <= min(mid, R); ++i){
		if (w(i, mid) > maxn)maxn = w(i, mid), pos = i;
	}
	dp[mid] = max(dp[mid], maxn);
	solve(l, mid - 1, L, pos);
	solve(mid + 1, r, pos, R);
}
signed main(){
	cin.tie(0)->sync_with_stdio(0);
	cin >> n;
	for (int i = 1; i <= n; ++i){
		cin >> a[i];
		sq[i] = sqrt(i);
	}
	solve(1, n, 1, n);
	for (int i = 1; i <= (n >> 1); ++i){
		swap(a[i], a[n - i + 1]);
		swap(dp[i], dp[n - i + 1]);
	}
	solve(1, n, 1, n);
	for (int i = n; i >= 1; --i){
		cout << (int)ceil(dp[i]) - a[i] << "\n";
	}
	return 0;
}

参考

决策单调性优化 DP 学习笔记

posted @ 2025-07-27 15:49  __int127  阅读(37)  评论(0)    收藏  举报