[区间dp]题解 P1880 石子合并
[NOI1995] 石子合并
题目描述
在一个圆形操场的四周摆放 \(N\) 堆石子,现要将石子有次序地合并成一堆,规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出一个算法,计算出将 \(N\) 堆石子合并成 \(1\) 堆的最小得分和最大得分。
输入格式
数据的第 \(1\) 行是正整数 \(N\),表示有 \(N\) 堆石子。
第 \(2\) 行有 \(N\) 个整数,第 \(i\) 个整数 \(a_i\) 表示第 \(i\) 堆石子的个数。
输出格式
输出共 \(2\) 行,第 \(1\) 行为最小得分,第 \(2\) 行为最大得分。
样例 #1
样例输入 #1
4
4 5 9 4
样例输出 #1
43
54
提示
\(1\leq N\leq 100\),\(0\leq a_i\leq 20\)。
思路
最近一直再做一些 dp 的题目,那么这道经典的区间 dp 当然是不能放过,这里写一下此题的题解。
区间 dp 定义状态这一步还是比较简单的,首先肯定能想到定义一个 \(dp_{l,r}\) 这样的状态表示,然后我们只需要,思考一下如何转移方程就行了。
还是套路的枚举一下间断点在哪里。假如是 \(k\) 。
突然发现我们需要预处理一个数组 \(sum_{l,r}\) 表示 \(l\) 到 \(r\) 中数字的和。这样的话我们就很好办了。反正总共的时间复杂度已经大于 \(\mathbb{O}(n^2)\) 了,那么我们这样预处理也问题不大,反正也不会让时间复杂度再增大到哪里去。当然是可以通过前缀和优化到 \(\mathbb{O}(n)\) 的。
状态转移方程显然为:
这里我们只写求最大值的,最小值的相反,但是要注意处理一下初值,我在这卡了 20min 。
\(dp_{l,r}=max\{dp_{l,k}+dp_{k+1,r}+sum_{l,k}+sum_{k+1,r}\} k \in [l,r)\)
非常显然,把两个东西合并起来所需要的代价加起来就 ok 了。
代码
#include <bits/stdc++.h>
#define debug puts("I love Newhanser forever!!!!!");
#define pb push_back
using namespace std;
template <typename T>inline void read(T& t){
t=0; register char ch=getchar(); register int fflag=1;
while(!('0'<=ch&&ch<='9')){if(ch=='-') fflag=-1;ch=getchar();}
while(('0'<=ch&&ch<='9')){t=t*10+ch-'0'; ch=getchar();} t*=fflag;
}
template <typename T,typename... Args> inline void read(T& t, Args&... args){read(t);read(args...);}
const int MAXN=386;
int n,a[MAXN];
int dp[MAXN][MAXN],sum[MAXN][MAXN],maxn,f[MAXN][MAXN],minn=0x3f3f3f3f;
int main(){
read(n);
memset(f,0x3f,sizeof(f));
for(int i=1;i<=n;++i) read(a[i]),a[i+n]=a[i],sum[i][i]=a[i],f[i][i]=0,f[i+n][i+n]=0,sum[i+n][i+n]=a[i];
for(int len=1;len<=n;++len)
for(int l=1;l<n*2-len;++l){
int r=l+len;
sum[l][r]=sum[l][r-1]+a[r];
}
for(int len=1;len<=n;++len)
for(int l=1;l<n*2-len;++l){
int r=l+len;
for(int k=l;k<r;++k){
f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]+sum[l][k]+sum[k+1][r]);
dp[l][r]=max(dp[l][r],dp[l][k]+dp[k+1][r]+sum[l][k]+sum[k+1][r]);
}
}
for(int i=1;i<=n;++i) maxn=max(maxn,dp[i][i+n-1]);
for(int i=1;i<=n;++i) minn=min(minn,f[i][i+n-1]);
cout<<minn<<endl<<maxn<<endl;
return 0;
}
//Welcome back,Chtholly.