1030 石子合并 区间DP 前缀和 环
链接:https://ac.nowcoder.com/acm/contest/24213/1030
来源:牛客网
题目描述
将n堆石子绕圆形操场排放,现要将石子有序地合并成一堆。规定每次只能选相邻的两堆合并成新的一堆,并将新的一堆的石子数记做该次合并的得分。
请编写一个程序,读入堆数n及每堆的石子数,并进行如下计算:
请编写一个程序,读入堆数n及每堆的石子数,并进行如下计算:
- 选择一种合并石子的方案,使得做n-1次合并得分总和最大。
- 选择一种合并石子的方案,使得做n-1次合并得分总和最小。
输入描述:
输入第一行一个整数n,表示有n堆石子。
第二行n个整数,表示每堆石子的数量。
输出描述:
第一行为合并得分总和最小值,
第二行为合并得分总和最大值。
备注:
对于100%100 \%100%的数据,有1≤n≤2001 \leq n \leq 2001≤n≤200。
分析
石子合并,经典的小区间到大区间问题,且时间复杂度是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; } /*样例区 */ //------------------------------------------------------------