题目链接:
[NOI1995] 石子合并 - 洛谷https://www.luogu.com.cn/problem/P1880参考了博客:【洛谷P1880】合并石子_sdfzchy的博客-CSDN博客
思路:
典型区间dp题,以求最小值为例,令dp[ i ][ j ]表示从 i 到 j 区间的石子合并得到的最小得分,当前dp值 = 合并的两堆本来就有的dp值之和 + 当前合并得到的dp值(sum[ i ][ j ])。
dp递推式如下:
细节处理:
1.要模拟环形,需要把n长度的数组扩展到2*n-1 。
2.sum可以用前缀和计算。
代码:
#include <iostream>
#include <queue>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 2e2+5; //实际maxn的两倍(因为要模拟环形)
int n; //石子数量
int sum[maxn]; //重量前缀和
int dp[maxn][maxn]; //区间
int dpMin(){
memset(dp, inf, sizeof(dp));
for(int i=1; i<=2*n-1; i++) dp[i][i] = 0;
for(int len=2; len<=n; len++){
for(int s=1; s<=2*n-len; s++){
int e = s+len-1;
for(int k=s; k<e; k++){
dp[s][e] = min(dp[s][e], dp[s][k]+dp[k+1][e]);
}
dp[s][e] += sum[e] - sum[s-1];
}
}
int ans = inf;
for(int i=1; i<=n; i++){
ans = min(ans, dp[i][i+n-1]);
}
return ans;
}
int dpMax(){
memset(dp,0,sizeof(dp));
for(int len=2; len<=n; len++){
for(int s=1; s<=2*n-len; s++){
int e = s+len-1;
for(int k=s; k<e; k++){
dp[s][e] = max(dp[s][e], dp[s][k]+dp[k+1][e]);
}
dp[s][e] += sum[e] - sum[s-1];
}
}
int ans = 0;
for(int i=1; i<=n; i++){
ans = max(ans, dp[i][i+n-1]);
}
return ans;
}
int main(){
cin >> n;
for(int i=1; i<=n; i++){//输入(注意环形)
cin >> sum[i];
sum[i+n] = sum[i];
}
for(int i=1; i<=2*n-1; i++){ //求前缀和
sum[i] += sum[i-1];
}
cout << dpMin() << endl;
cout << dpMax() << endl;
}