洛谷 P1880 石子合併

2022-06-26 06:03:10 字數 1418 閱讀 1364

一道區間 dp的典型題目。

下面,我們先考慮不在環上,而在一條鏈上的情況。

令狀態 \(f(i,j)\) 表示將下標在 \([i,j]\) 區間的元素合併起來所能獲得的最大價值,則 \(f(1,n)\) 就是問題的答案。狀態轉移式為:

\[f(i,j)=\max\,\quad k\in[i,j)

\]\(cost(i,j,k)\) 表示將區間 \([i,k]\) 和 \([k+1,j]\) 合併為 \([i,j]\) 的代價,這裡的 \(k\) 就是要列舉的合併點。

使用遞推法求解區間 dp 時,通常的做法是從小到大列舉區間長度。這樣能保證在求解大區間時,小區間的答案已經被求解出來了。

演算法模板如下,時間複雜度 \(o(n^3)\)。

for (int len = 2; len <= n; len++)

}}

現在讓我們回到原問題,怎麼處理在環上的情況呢?

如果是在乙個長為 \(n\) 的環上,那麼弄一條長為 \(2n\) 的鏈(重複一次),dp 求解後取 \(f(1,n),f(2,n+1),\ldots,f(n-1,2n-1)\) 中的最優值即可。時間複雜度仍為 \(o(n^3)\)。

參考資料:區間 dp - oi wiki

#include #include #include using namespace std;

const int inf = int(1e9);

const int maxn = 100 + 5;

int dp_min[maxn * 2][maxn * 2];

int dp_max[maxn * 2][maxn * 2];

int arr[maxn * 2];

int prefix_sum[maxn * 2];

//由於我們要把鏈重複一次,所以陣列要開兩倍大小

//預處理出字首和

void calc_prefix_sum(int n)

//[x, y] 的區間和

int sum(int x, int y)

int main()

calc_prefix_sum(n);

//遞推求解

for (int len = 2; len <= n; len++)

dp_min[i][j] = temp_min;

dp_max[i][j] = temp_max;}}

int ans_min = dp_min[1][n];

int ans_max = dp_max[1][n];

for (int i = 2; i <= n - 1; i++)

printf("%d\n%d", ans_min, ans_max);

return 0;

}

洛谷P1880 石子合併

描述 在乙個園形操場的四周擺放n堆石子,現要將石子有次序地合併成一堆.規定每次只能選相鄰的2堆合併成新的一堆,並將新的一堆的石子數,記為該次合併的得分。試設計出1個演算法,計算出將n堆石子合併成1堆的最小得分和最大得分.輸入格式 資料的第1行試正整數n,1 n 100,表示有n堆石子.第2行有n個數...

洛谷 P1880 石子合併

題目描述 在乙個園形操場的四周擺放n堆石子,現要將石子有次序地合併成一堆.規定每次只能選相鄰的2堆合併成新的一堆,並將新的一堆的石子數,記為該次合併的得分。試設計出1個演算法,計算出將n堆石子合併成1堆的最小得分和最大得分.輸入輸出格式 輸入格式 資料的第1行試正整數n,1 n 100,表示有n堆石...

洛谷 P1880 石子合併 環形

在乙個圓形操場的四周擺放n堆石子,現要將石子有次序地合併成一堆.規定每次只能選相鄰的2堆合併成新的一堆,並將新的一堆的石子數,記為該次合併的得分。試設計出1個演算法,計算出將n堆石子合併成1堆的最小得分和最大得分.輸入格式 資料的第1行試正整數n,1 n 100,表示有n堆石子.第2行有n個數,分別...