1030 石子合并 区间DP
作者:互联网
链接:https://ac.nowcoder.com/acm/contest/24213/1030
来源:牛客网
题目描述
将n堆石子绕圆形操场排放,现要将石子有序地合并成一堆。规定每次只能选相邻的两堆合并成新的一堆,并将新的一堆的石子数记做该次合并的得分。请编写一个程序,读入堆数n及每堆的石子数,并进行如下计算:
- 选择一种合并石子的方案,使得做n-1次合并得分总和最大。
- 选择一种合并石子的方案,使得做n-1次合并得分总和最小。
输入描述:
输入第一行一个整数n,表示有n堆石子。
第二行n个整数,表示每堆石子的数量。
输出描述:
第一行为合并得分总和最小值,示例1
第二行为合并得分总和最大值。
输入
复制4 4 5 9 4
输出
复制43 54
备注:
对于100%100 \%100%的数据,有1≤n≤2001 \leq n \leq 2001≤n≤200。
分析
石子合并,经典的小区间到大区间问题,且时间复杂度是n^2 ,所以区间DP
用前缀和维护,可以算出合并一段区间所需要的能量
易错点
1.石子数量从2开始取
2.算最小值的时候,要把当前要算的区间的DP值置无穷
//-------------------------代码----------------------------
//#define int LL
const int N = 500;
int n,m;
int a[N],f[N][N],dp[N][N];
void solve()
{
// ms(f,inf);
cin>>n;
for(int i = 1;i <= n;i ++){
cin>>a[i];
a[i + n] = a[i];
}
for(int i = 1;i <= 2 * n;i ++){
a[i] += a[i - 1];
}
int mx = -inf,mn = inf;
for(int k = 2;k<=n;k++) {
for(int i = 1,j = k;j<=2*n;j++,i++) {
f[i][j] = inf;//要注意当前值一定要无穷大,其它值一定要0,才能转化。。
for(int k = i;k<j;k++) {
f[i][j] = min(f[i][j],f[i][k] + f[k+1][j] + a[j] - a[i-1]);
dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+a[j]-a[i-1]);
}
}
}
fo(i,1,n) {
mx = max(mx,dp[i][i + n - 1]);
mn = min(mn,f[i][i + n - 1]);
}
cout<<mn<<endl<<mx<<endl;
}
signed main(){
clapping();TLE;
// int t;cin>>t;while(t -- )
solve();
// {solve(); }
return 0;
}
/*样例区
*/
//------------------------------------------------------------
标签:得分,int,石子,合并,DP,1030,inf,dp 来源: https://www.cnblogs.com/er007/p/16468593.html