石子合併 區間dp初感悟

2022-09-09 18:03:27 字數 2114 閱讀 7900

感覺區間dp和線性dp差別不大,區間dp和線性dp的差別應該是在求解的順序有所改變,以石子合併為例

題目描述

設有n堆石子排成一排,其編號為1,2,3,…,n,每堆石子有一定的質量,可以用一			  

個整數來描述,現在要將這n堆石子合併成為一堆,每次只能合併相鄰的兩堆,合併的

代價為這兩堆石子的質量之和,合併後與這兩堆石子相鄰的石子將和新堆相鄰,合併時

由於選擇的順序不同,合併的總代價也不相同。

例如有4堆石子分別為1 3 5 2

我們可以先合併1、2堆,代價為4,得到4 5 2,

又合併1,2堆,代價為9,得到9 2,再合併得到11,總代價為4+9+11=24;

如果第二步是先合併2,3堆,則代價為7,得到4 7

最後一次合併代價為11,總代價為4+7+11=22。

問題是:找出一種合理的方法,使總的代價最小,輸出最小代價。

f[i][j]表示將i~j堆石子合併起來的最小代價

那麼我們只需想i ~ j堆石子的合併是怎麼得到的

i ~ j堆石子的合併,必將由 i ~ k堆石子合併後與 k + 1 ~ j堆石子合併後

再合併而得到(i <= k <= j - 1)

得出狀態轉移方程:

前提:i <= k <= j - 1;

p1 = pre[k] - pre[i - 1];

p2 = pre[j] - pre[k];

f[i][j] = min(f[i][k] + f[k + 1][j] + p1 + p2)

我們的迴圈順序是什麼???

這個???

for

(int i =

1; i <= n; i++

)//列舉左端點

for(

int j = i +

1; j <= n; j++

)//列舉右端點

for(

int k = i; k <= j -

1; k++

)//列舉「斷」點

很容易發現:在計算f[i][j]這個狀態時,它的「源頭」可能是還沒有計算出來的。

不妨手算一下

1.

p1 = pre[1] - pre[0]

p2 = pre[2] - pre[1]

f[1][2] = min(f[1][1] + f[2][2] + p1 + p2)

2.p1 = pre[1] - pre[0]

p2 = pre[3] - pre[1]

f[1][3] = min(f[1][1] + f[2][3] + p1 + p2)

3....

我們通過觀察,會發現乙個重要的事情:

當我們求f[1][3]時,我們將用到f[2][3],而我們此時並沒有求出f[2][3]

所以狀態轉移的順序是有問題

正確的順序是這樣的

scanf

("%d"

,&n)

;for

(int i =

1; i <= n; i++

)for

(int i =

1; i <= n; i++)}

}

#include

const

int maxn =

305;

const

int inf =

0x3f3f3f3f

;int n;

int a[maxn]

, pre[maxn]

;int f[maxn]

[maxn]

;//f[i][j]表示將i~j堆石子合併起來的最小代價

intmin

(int x,

int y)

intmain()

for(

int i =

1; i <= n; i++)}

}printf

("%d"

, f[1]

[n])

;return0;

}

石子合併 (區間DP

問題描述 在乙個操場上擺放著一行共n堆的石子。現要將石子有序地合併成一堆。規定每次只能選相鄰的兩堆合併成新的一堆,並將新的一堆石子數記為該次合併的得分。請編輯計算出將n堆石子合併成一堆的最小得分和將n堆石子合併成一堆的最大得分。輸入檔案 輸入第一行為n n 1000 表示有n堆石子,第二行為n個用空...

石子合併 區間dp

有n堆石子排成一排,每堆石子有一定的數量。現要將n堆石子並成為一堆。合併的過程只能每次將相鄰的兩堆石子堆成一堆,每次合併花費的代價為這兩堆石子的和,經過n 1次合併後成為一堆。求出總的代價最小值。假設dp 1 4 表示將區間1 4的石子合併所花費的代價。dp 1 4 可以劃分為dp 1 1 dp 2...

石子合併 (區間DP)

題目鏈結 描述 有n堆石子排成一排,每堆石子有一定的數量。現要將n堆石子並成為一堆。合併的過程只能每次將相鄰的兩堆石子堆成一堆,每次合併花費的代價為這兩堆石子的和,經過n 1次合併後成為一堆。求出總的代價最小值。輸入第一行有乙個整數n,表示有n堆石子。接下來的一行有n 0 n 200 個數,分別表示...