1030 石子合并 区间DP 前缀和 环

链接:https://ac.nowcoder.com/acm/contest/24213/1030
来源:牛客网

题目描述

将n堆石子绕圆形操场排放,现要将石子有序地合并成一堆。规定每次只能选相邻的两堆合并成新的一堆,并将新的一堆的石子数记做该次合并的得分。
请编写一个程序,读入堆数n及每堆的石子数,并进行如下计算:
  1. 选择一种合并石子的方案,使得做n-1次合并得分总和最大。
  2. 选择一种合并石子的方案,使得做n-1次合并得分总和最小。

输入描述:

输入第一行一个整数n,表示有n堆石子。
第二行n个整数,表示每堆石子的数量。

输出描述:

第一行为合并得分总和最小值,
第二行为合并得分总和最大值。
示例1

输入

复制
4
4 5 9 4

输出

复制
43
54

备注:

对于100%100 \%100%的数据,有1≤n≤2001 \leq n \leq 2001n200。

分析

石子合并,经典的小区间到大区间问题,且时间复杂度是n^2 ,所以区间DP

不过这题的特殊点在于绕环放石子,所以跑一边2*n,把以某个点 为段点的所有情况跑出来就可以了

用前缀和维护,可以算出合并一段区间所需要的能量

 

易错点

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;
}
 
/*样例区
 
 
*/
 
//------------------------------------------------------------

 

posted @ 2022-07-12 03:56  er007  阅读(27)  评论(0编辑  收藏  举报