區間DP BZOJ 3229石子合併

Description

  在一個操場上擺放着一排N堆石子。現要將石子有次序地合併成一堆。規定每次只能選相鄰的2堆石子合併成新的一堆,並將新的一堆石子數記爲該次合併的得分。

  試設計一個算法,計算出將N堆石子合併成一堆的最小得分.

Input

  第一行是一個數N。以下N行每行一個數A,表示石子數目。

Output

  共一個數,即N堆石子合併成一堆的最小得分。

Sample Input

4
1
1
1
1
 

Sample Output

8

HINT

 

對於 100% 的數據,1≤N≤40000

對於 100% 的數據,1≤A≤200

 

解題思路:題目很簡單,但是沒辦法用常規的區間dp來做 (n^3) 這時候我們需要優化一下。

GarsiaWachs算法

平行四邊形優化區間dp

四邊形不等式優化動態規劃原理:

1.當決策代價函數w[i][j]滿足w[i][j]+w[i’][j’]<=w[I;][j]+w[i][j’](i<=i’<=j<=j’)時,稱w滿足四邊形不等式.當函數w[i][j]滿足w[i’][j]<=w[i][j’] i<=i’<=j<=j’)時,稱w關於區間包含關係單調.

2.如果狀態轉移方程m爲且決策代價w滿足四邊形不等式的單調函數(可以推導出m亦爲滿足四邊形不等式的單調函數),則可利用四邊形不等式推出最優決策s的單調函數性,從而減少每個狀態的狀態數,將算法的時間複雜度由原來的O(n^3)降低爲O(n^2).方法是通過記錄子區間的最優決策來減少當前的決策量.令:

s[i][j]=max{k | ma[i][j] = m[i][k-1] + m[k][j] + w[i][j]}

由於決策s具有單調性,因此狀態轉移方程可修改爲:

證明過程: (轉載)

設m[i,j]表示動態規劃的狀態量。

m[i,j]有類似如下的狀態轉移方程:

m[i,j]=opt{m[i,k]+m[k,j]}(i≤k≤j)

如果對於任意的a≤b≤c≤d,有m[a,c]+m[b,d]≤m[a,d]+m[b,c],那麼m[i,j]滿足四邊形不等式。

以上是適用這種優化方法的必要條件

對於一道具體的題目,我們首先要證明它滿足這個條件,一般來說用數學歸納法證明,根據題目的不同而不同。

通常的動態規劃的複雜度是O(n3),我們可以優化到O(n2)

s[i,j]m[i,j]的決策量,即m[i,j]=m[i,s[i,j]]+m[s[i,j]+j]

我們可以證明,s[i,j-1]≤s[i,j]≤s[i+1,j]  (證明過程見下)

那麼改變狀態轉移方程爲:

m[i,j]=opt{m[i,k]+m[k,j]}      (s[i,j-1]≤k≤s[i+1,j])

複雜度分析:不難看出,複雜度決定於s的值,以求m[i,i+L]爲例,

(s[2,L+1]-s[1,L])+(s[3,L+2]-s[2,L+1])…+(s[n-L+1,n]-s[n-L,n-1])=s[n-L+1,n]-s[1,L]≤n

所以總複雜度是O(n2)

對s[i,j-1]≤s[i,j]≤s[i+1,j]的證明:

設mk[i,j]=m[i,k]+m[k,j],s[i,j]=d

對於任意k<d,有mk[i,j]≥md[i,j](這裏以m[i,j]=min{m[i,k]+m[k,j]}爲例,max的類似),接下來只要證明mk[i+1,j]≥md[i+1,j],那麼只有當s[i+1,j]≥s[i,j]時纔有可能有ms[i+1,j][i+1,j]≤md[i+1,j]

(mk[i+1,j]-md[i+1,j]) - (mk[i,j]-md[i,j])

=(mk[i+1,j]+md[i,j]) - (md[i+1,j]+mk[i,j])

=(m[i+1,k]+m[k,j]+m[i,d]+m[d,j]) - (m[i+1,d]+m[d,j]+m[i,k]+m[k,j])

=(m[i+1,k]+m[i,d]) - (m[i+1,d]+m[i,k])

∵m滿足四邊形不等式,∴對於i<i+1≤k<d有m[i+1,k]+m[i,d]≥m[i+1,d]+m[i,k]

∴(mk[i+1,j]-md[i+1,j])≥(mk[i,j]-md[i,j])≥0

∴s[i,j]≤s[i+1,j],同理可證s[i,j-1]≤s[i,j]

證畢

擴展:

以上所給出的狀態轉移方程只是一種比較一般的,其實,很多狀態轉移方程都滿足四邊形不等式優化的條件。

解決這類問題的大概步驟是:

0.證明w滿足四邊形不等式,這裏w是m的附屬量,形如m[i,j]=opt{m[i,k]+m[k,j]+w[i,j]},此時大多要先證明w滿足條件才能進一步證明m滿足條件

1.證明m滿足四邊形不等式

2.證明s[i,j-1]≤s[i,j]≤s[i+1,j]

 

設序列是stone[],從左往右,找一個滿足stone[k-1] <= stone[k+1]的k,找到後合併stone[k]和stone[k-1],再從當前位置開始向左找最大的j,使其滿足stone[j] > stone[k]+stone[k-1],插到j的後面就行。一直重複,直到只剩下一堆石子就可以了。在這個過程中,可以假設stone[-1]和stone[n]是正無窮的。

舉個例子:186 64 35 32 103

因爲35<103,所以最小的k是3,我們先把35和32刪除,得到他們的和67,並向前尋找一個第一個超過67的數,把67插入到他後面,得到:186 67 64 103,現在由5個數變爲4個數了,繼續:186 131 103,現在k=2(別忘了,設A[-1]和A[n]等於正無窮大)234 186,最後得到420。最後的答案呢?就是各次合併的重量之和,即420+234+131+67=852。

基本思想是通過樹的最優性得到一個節點間深度的約束,之後證明操作一次之後的解可以和原來的解一一對應,並保證節點移動之後他所在的深度不會改變。具體實現這個算法需要一點技巧,精髓在於不停快速尋找最小的k,即維護一個“2-遞減序列”樸素的實現的時間複雜度是O(n*n),但可以用一個平衡樹來優化,使得最終複雜度爲O(nlogn)。

 

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn=4e4+10;
int n,m,num[maxn],t,ans;
void combine(int k){
	int tmp=num[k]+num[k-1];
	ans+=tmp;
	for(int i=k;i<t-1;i++)
		num[i]=num[i+1];
	t--;
	int j=0;
	for(j=k-1;j>0&&num[j-1]<tmp;j--){
		num[j]=num[j-1];
	}
	num[j]=tmp;
	while(j>=2&&num[j]>=num[j-2]){
		int d=t-j;
		combine(j-1);
		j=t-d;
	}
}
int main(){
	int i,j;
	while(scanf("%d",&n)!=EOF){
		for(i=0;i<n;i++) scanf("%d",&num[i]);
		t=1;ans=0;
		for(i=1;i<n;i++){
			num[t++]=num[i];
			while(t>=3&&num[t-3]<=num[t-1]){
				combine(t-2);
			}
		}
		while(t>1) combine(t-1);
		printf("%d\n",ans);
	}
	return 0;
}

 

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