【學習總結】DP優化:決策單調性 ,四邊形不等式,convex hull trick及其應用

camp的講義

決策單調性

普通單調性

分治或者二分解決

四邊形不等式

參考這篇文章

f(i,j)=minf(i,k)+f(k,j)+w(i,j) f(i,j) = minf(i,k) + f(k,j) + w(i,j)

若w(i,j)滿足:

  1. 相互包含的區間,更大的區間更劣
  2. 包含劣於交叉
    或者用XDU_Skyline 的表述:
    (1)區間包含的單調性:如果對於i≤i’<j≤j’,有w(i’,j)≤w(i,j’),那麼說明w具有區間包含的單調性。(可以形象理解爲如果小區間包含於大區間中,那麼小區間的w值不超過大區間的w值)
    (2)四邊形不等式:如果對於i≤i’<j≤j’,有w(i,j)+w(i’,j’)≤w(i’,j)+w(i,j’),我們稱函數w滿足四邊形不等式。(可以形象理解爲兩個交錯區間的w的和不超過小區間與大區間的w的和)

則:f(i,j)滿足四邊形不等式。且滿足凸性:g(i)=f(i,j)f(i,j+1)&lt;=g(i+1)=f(i+1,j)f(i+1,j+1)g(i) = f(i,j) - f(i,j + 1)&lt;=g(i + 1) = f(i + 1,j) - f(i + 1,j + 1)
即區間長度更大的差也更大

有了這些性質,可以推導出決策點單調(證明見前面引用的博客):

p[l][r1]&lt;=p[l][r]&lt;=p[l+1][r] p[l][r- 1] &lt;= p[l][r] &lt;= p[l + 1][r]

注意事項

決策單調性要把“決策”定義清楚!如果沒有進行決策(沿用上一層決策),或者當前狀態不合法一定要小心
分治解決普通單調性問題時,若更大的區間不合法,但是小區間的決策點要沿用pr(不是pl)
決策點通常是最左邊的那個,並且上一段的最後一個(不是當前段的開頭)
滿足決策單調性則可行的決策點一定是連續的區間。
長度 <= 2的區間最好特判,要能夠讓長度== 3的區間能夠正確轉移即可。
不合法的狀態初值爲inf

例題

Petrozavodsk Winter-2016. JAG Contest

題意
設計不超過k輪的比賽,使得決出冠軍,無聊度最低。
無聊度 = AiAj\sum{|Ai - Aj|} , Ai爲選手能力值
k <= 50 , n <= 1000

直接四邊形不等式
要注意f[k][l][r]表示的至多k層的時候的最優答案,如果不足k層要最後轉移
如果一開始就從f[k - 1][l][r]轉移,會影響決策,因爲這時的決策點未定義。

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1e3 + 20;
int n,k,a[maxn],f[2][maxn][maxn],p[maxn][maxn];

int main(){
	scanf("%d %d",&n,&k);
	for (int i = 1 ;i <= n ; i++) scanf("%d",&a[i]);
	sort(a + 1,a + n + 1);
	memset(f,0x3f,sizeof(f));
	int now = 0,last;
	for (int i = 1 ; i <= n ; i++) f[now][i][i] = 0 , f[now][i - 1][i] = a[i] - a[i - 1] , p[i][i] = i , p[i - 1][i] = i - 1;
	
	for (int t = 2 ; t <= k ; t++){
		last = now , now ^= 1;
		memset(f[now],0x3f,sizeof(f[now]));
		memset(p,0,sizeof(p));
		//cout<<"check : "<<t<<endl;
		for (int i = 1 ; i <= n ; i++) f[now][i][i] = 0 , f[now][i][i + 1] = a[i + 1] - a[i] , p[i][i + 1] = i;
		for (int len = 3 ; len <= n ; len++){
			for (int l = 1 ; l <= n - len + 1 ; l++){
				int r = l + len - 1;
				//f[now][l][r] = inf;
				for (int s = p[l][r - 1] ; s <= p[l + 1][r] ; s++){
					if ( f[now][l][r] > f[last][l][s] + f[last][s + 1][r] + a[r] - a[s] ){
						f[now][l][r] = f[last][l][s] + f[last][s + 1][r] + a[r] - a[s];
						p[l][r] = s;
					}
				}
			//	if(p[l + 1][r] < p[l][r])cout<<l<<" "<<r<<" "<<p[l][r]<<" "<<" "<<p[l][r - 1]<<" "<<p[l + 1][r]<<" "<<f[now][l][r]<<endl;
				f[now][l][r] = min(f[now][l][r],f[last][l][r]);
			}
		}
	}
	cout<<f[now][1][n]<<endl;
}

訓練的時候四邊形不等式寫錯了,寫了一個普通的決策單調性
注意當決策區間不合法時,比該區間小的區間是合法的。
所以應該讓小區間保留[pl,pr]的決策點區間,而所有更大的區間都不合法

#include<bits/stdc++.h>
using namespace std;
#define int long long 

const int maxn = 1e3 + 20;
//const int inf = 1e10;
int n,k,a[maxn],f[2][maxn][maxn],p[2][maxn][maxn];
int now = 0,last;


void solve(int k,int l,int r,int pl,int pr){

	if ( l > r ) return;
	//	cout<<k<<" "<<l<<" "<<r<<" "<<pl<<" "<<pr<<endl;

	int mid = (l + r) >> 1,id = pr; //若不合法,小區間決策點仍然是[pl,pr],大區間都不合法
//	f[now][k][mid] = inf * 2;
	for (int i = pl ; i <= pr and i < mid ; i++){
		long long  d = (long long)f[last][k][i] + f[last][i + 1][mid] + a[mid] - a[i];
		if ( f[now][k][mid] > d ){
			f[now][k][mid] = d;
			id = i;
		}
	}
	solve(k,l,mid - 1,pl,id);
	solve(k,mid + 1,r,id,pr);
}
signed main(){
//	freopen("input.txt","r",stdin);
	scanf("%lld %lld",&n,&k);
	for (int i = 1 ;i <= n ; i++) scanf("%lld",&a[i]);
	sort(a + 1,a + n + 1);
	//for (int i = 0 ; i <= n ; i++) for (int j = 0 ; j <= n ; j++) f[0][i][j] = f[1][i][j] = inf;
	memset(f,0x3f,sizeof(f));
	for (int i = 1 ; i <= n ; i++) f[now][i][i] = 0 , f[now][i - 1][i] = a[i] - a[i - 1];
	
	for (int t = 2 ; t <= k ; t++){
		last = now , now ^= 1;
		memset(f[now],0x3f,sizeof(f[now]));
		//for (int i = 0 ; i <= n ; i++) for (int j = 0 ; j <= n ; j++) f[now][i][j] = 3 * inf;
		for (int i = 1 ; i <= n ; i++) f[now][i][i] = 0 , f[now][i][i + 1] = a[i + 1] - a[i];

		for (int l = 1 ; l <= n - 1 ; l++)
			solve(l,l + 2,n,l,n);
	//	cout<<"check "<<t<<endl;
		f[now][n][n] = 0;
		for (int l = 0 ; l <= n ; l++)
			for (int r = 0 ; r <= n ; r++){
				f[now][l][r] = min(f[now][l][r],f[last][l][r]);
				
			}

	}
	cout<<f[now][1][n]<<endl;
}

convex hull trick

以下內容based on camp的講義

斜率優化

這是所有的決策函數都爲直線的情況。形如:
首先看一個簡單的版本
d(i,j)=min1k&lt;jd(i1,k)+ai,k+bi,k(jk) d(i, j)=\min _{1 \leq k&lt;j} d(i-1, k)+a_{i, k}+b_{i, k} \cdot(j-k)

Note that the value d(i, j) is defined as the minimum among linear functions, thus we
can build the lower envelope if O(sort(n) + n) time (O(n log n) in general).

d(i)=min1j&lt;id(j)+aj+bj(ij) d(i)=\min _{1 \leq j&lt;i} d(j)+a_{j}+b_{j} \cdot(i-j)
我們很多題目bjb_{j}都單調,直接按順序加入維護凸殼。不單調也是可以做得(不是動態凸殼)

We can still achieve O(n log n) complexity
by inserting the new element in the convex hull, or using O(log n) separate convex hulls
instead of one. This means, build a segment tree, each node of the tree is computed only when all the values d(i) for this node are known.

直接用線段樹類似的結構維護,只有一層的所有節點都加滿後才合併。
可以暴力歸併。複雜度顯然正確。
查詢的時候相當於區間查詢

更加一般化的形式

Note, that we actually never used the fact that what we are given is a linear function.
Instead, we can consider:
d(i)=min1j&lt;id(j)+c(j,i) d(i)=\min _{1 \leq j&lt;i} d(j)+c(j, i)
 Sufficient condition: for every two i and j there exists some k such that d(i)+c(i,x)d(j)+c(j,x) for all xk and d(i)+c(i,x)&gt;d(j)+c(j,x) for all x&gt;k (or vice  versa). In other words, function growing from position i overtakes function growing from j exactly once.  \begin{array}{l}{\text { Sufficient condition: for every two } i \text { and } j \text { there exists some } k \text { such that } d(i)+} \\ {c(i, x) \leq d(j)+c(j, x) \text { for all } x \leq k \text { and } d(i)+c(i, x)&gt;d(j)+c(j, x) \text { for all } x&gt;k \text { (or vice }} \\ {\text { versa). In other words, function growing from position } i \text { overtakes function growing from }} \\ {j \text { exactly once. }}\end{array}

注意兩個價值函數之間只有一個交點,不意味着決策點單調。需要特殊函數才滿足
需要快速計算這個交點。如果是函數可以直接計算,否則需要二分
維護一個棧:這些交點的排序,查詢直接在棧上二分。同斜率優化

 Exampe problem: you have n groups of items for knapsack problem and you want  to get the maximum total cost without exceeding total weight of L. For each group you  know that the weight of all items in the group is wi, but the costs are ci,1,ci,2,,ci,sisi=m. Solve the knapsack problem in O(nLlogL+mlogm) time.  \begin{array}{l}{\text { Exampe problem: you have } n \text { groups of items for knapsack problem and you want }} \\ {\text { to get the maximum total cost without exceeding total weight of } L . \text { For each group you }} \\ {\text { know that the weight of all items in the group is } w_{i}, \text { but the costs are } c_{i, 1}, c_{i, 2}, \ldots, c_{i, s_{i}}} \\ {\sum s_{i}=m . \text { Solve the knapsack problem in } O(n L \log L+m \log m) \text { time. }}\end{array}

例題

一道還不會做的題

如果給轉移的函數加上一個區間的限制,如:
f(i)=max(f(j)+(ij)k),i&lt;=R(j) f(i) = max(f(j) + (i - j)^k) , i &lt;= R(j)
有沒有優美的做法?

首先如果是min的話很好做。因爲超過界限設爲inf就好。

然後在一般化爲 f(j)+g(ij)f(j) + g(i-j) , 滿足g(i)的導數遞增

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章