感覺區間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 個數,分別表示...