码迷,mamicode.com
首页 > 其他好文 > 详细

P1880 [NOI1995]石子合并

时间:2018-04-29 17:48:49      阅读:180      评论:0      收藏:0      [点我收藏+]

标签:badge   长度   family   合并   -o   using   ons   i+1   str   

题目描述

在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。

试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.

输入输出格式

输入格式:

数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.

输出格式:

输出共2行,第1行为最小得分,第2行为最大得分.

输入输出样例

输入样例#1: 复制
4
4 5 9 4
输出样例#1: 复制
43
54

 

Solution:

  本题不能贪心,随便举举就可以发现。

  考虑区间$DP$,设$f[i][j]$表示$i-j$合并的花费,$sum[i]$表示到$i$的花费前缀和,由于是环形,直接破环成链在原数组后面复制一下原数组,则不难得到状态转移方程:$f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]),k\in[i,j)$,注意边界$f[i][i]=0,f[i][i+1]=s[i+1]-s[i-1]$,枚举区间长度递推,由于是环,所以目标状态为$f[i][n+i-1]$中的最小\最大值。

代码:

 

 1 #include<bits/stdc++.h>
 2 #define il inline
 3 #define ll long long
 4 #define Min(a,b) (a)>(b)?(b):(a)
 5 #define Max(a,b) (a)>(b)?(a):(b)
 6 #define f_for(a,b,i) for(int (i)=(a);(i)<=(b);(i)++)
 7 using namespace std;
 8 const int N=205;
 9 int n,a[N],s[N],f1[N][N],f2[N][N],ans1=520520520,ans2;
10 int main(){
11     ios::sync_with_stdio(0);
12     cin>>n;
13     memset(f1,0x3f,sizeof(f1));memset(f2,-0x3f,sizeof(f2));
14     f_for(1,n,i)cin>>a[i],a[i+n]=a[i],f1[i][i]=f2[i][i]=f1[i+n][i+n]=f2[i+n][i+n]=0;
15     f_for(1,(n<<1)-1,i)s[i]=a[i]+s[i-1];
16     f_for(1,n-1,l) 
17     f_for(1,(n<<1)-1,i){ 
18         int j=i+l;
19         if(j>((n<<1)))break;
20         if(l==1)f1[i][j]=f2[i][j]=s[j]-s[i-1];
21         else f_for(i,j-1,k)
22             f1[i][j]=Min(f1[i][j],f1[i][k]+f1[k+1][j]+s[j]-s[i-1]),
23             f2[i][j]=Max(f2[i][j],f2[i][k]+f2[k+1][j]+s[j]-s[i-1]);
24     }
25     f_for(1,n,i)
26         if((i+n-1)>((n<<1)))break;
27         else ans1=Min(ans1,f1[i][i+n-1]),ans2=Max(ans2,f2[i][i+n-1]);
28     cout<<ans1<<endl<<ans2;
29     return 0;
30 
31 }

 

P1880 [NOI1995]石子合并

标签:badge   长度   family   合并   -o   using   ons   i+1   str   

原文地址:https://www.cnblogs.com/five20/p/8971370.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!