合併石子問題
https://www.luogu.com.cn/problem/P1880
[NOI1995] 石子合併
題目大致描述
$N$堆石子擺成了一個圓,每相鄰的兩堆合成一堆,新的一堆的石子數為得分,求得分最小和最多
$1\leq N\leq 100$,$0\leq a_i\leq 20$。
解決思路:取每個區間的最大值
1、獲取資料,取得字首和
2、第一層for迴圈,用長度,從2到n
3、第二層for迴圈,用起點,
4、在第三層for迴圈前先確定終點,然後第三層迴圈把每一個小區間列舉對比
關鍵程式碼實現:
//第一層 長度
for(int len=2;len<=n;len++){
//第二層 起始點
for(int i=1;i<=n-len+1;i++){
//終點
int j=i+len-1;
//由於是最小值,先賦初值
dpmin[i][j] = 1e9;
//第三層 中間每一個最值
for(int k=i;k<j;k++){
//狀態轉移 本身與內部,但內部在合成後 還需要加上本次消耗的體力
dpmax[i][j]=max(dpmax[i][j],dpmax[i][k]+dpmax[k+1][j]+sum[j]-sum[i-1]);
dpmin[i][j]=min(dpmin[i][j],dpmin[i][k]+dpmin[k+1][j]+sum[j]-sum[i-1]);
}
}
}
但是由於是圓形,需要考慮越過陣列終點與陣列開頭合併,所以用兩倍陣列,可以包含所有情況:
#include<iostream>
using namespace std;
const int N = 210;
long dpmin[N][N],dpmax[N][N],sum[N];
int n;
int main()
{
cin >> n;
sum[0] = 0;
for (int i = 1; i <= n; i++){
cin >> sum[i];
sum[i + n] = sum[i];
sum[i] += sum[i - 1];
}
for (int i = n + 1; i <= 2 * n; i++)
sum[i] += sum[i - 1];
long mins = 1e9,maxs = 0;
for (int len = 2; len <= n ; len++){
for (int i = 1; i <= 2 * n - len + 1; i++){
int j = i + len - 1;
dpmin[i][j] = 1e9;
for (int k = i; k < j; k++){
dpmin[i][j] = min(dpmin[i][j], dpmin[i][k] + dpmin[k + 1][j] + sum[j] - sum[i - 1]);
dpmax[i][j] = max(dpmax[i][j], dpmax[i][k] + dpmax[k + 1][j] + sum[j] - sum[i - 1]);
}
if (len == k){
mins = min(mins, dpmin[i][j]);
maxs = max(maxs, dpmax[i][j]);
}
}
}
cout << mins << endl;
cout << maxs << endl;
return 0;
}