NIOP1995 石子合并(区间DP)
状态转移方程在代码中标出
本题注意是圆形,所以之前要预先处理一下s数组。处理之后总长度为2*n-1.第一个合并的起点有n个,所以总的方案数是n
注释在代码中标出
http://www.rqnoj.cn/problem/490
石子合并问题推荐几篇很好的文章
http://blog.csdn.net/acdreamers/article/details/18039073
http://blog.csdn.net/bnmjmz/article/details/41308919
注意四边形不等式优化,可以把n^3优化到n^2
1 //#pragma comment(linker, "/STACK:167772160")//手动扩栈~~~~hdu 用c++交 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<iostream> 6 #include<queue> 7 #include<stack> 8 #include<cmath> 9 #include<set> 10 #include<algorithm> 11 #include<vector> 12 // #include<malloc.h> 13 using namespace std; 14 #define clc(a,b) memset(a,b,sizeof(a)) 15 #define LL long long 16 const int Inf = 0x3f3f3f3f; 17 const double eps = 1e-5; 18 const double pi = acos(-1); 19 // inline int r(){ 20 // int x=0,f=1;char ch=getchar(); 21 // while(ch>'9'||ch<'0'){if(ch=='-') f=-1;ch=getchar();} 22 // while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} 23 // return x*f; 24 // } 25 const int Times = 10; 26 const int N = 5500; 27 int s[210],d[210][210],p[210][210],a[210];//d/p表示i到j的最小/大花费 28 int main(){ 29 int n; 30 int m; 31 s[0]=0; 32 while(~scanf("%d",&n)){ 33 clc(d,0); 34 clc(p,0); 35 for(int i=1;i<=n;i++){ 36 scanf("%d",&a[i]); 37 s[i]=s[i-1]+a[i]; 38 } 39 for(int i=1;i<n;i++){ 40 s[i+n]=s[n+i-1]+a[i]; 41 } 42 m=n,n=n*2-1; 43 int j,tem1,tem2; 44 for(int r=2;r<=n;r++){//枚举i到j的长度 45 for(int i=1;i<=n-r+1;i++){//i起始坐标,j表示当前枚举的终点坐标 46 j=i+r-1,tem1=Inf,tem2=0; 47 for(int k=i;k<j;k++){ 48 tem1=min(tem1,d[i][k]+d[k+1][j]+s[j]-s[i-1]); 49 tem2=max(tem2,p[i][k]+p[k+1][j]+s[j]-s[i-1]); 50 } 51 d[i][j]=tem1; 52 p[i][j]=tem2; 53 } 54 } 55 tem1=Inf,tem2=0; 56 for(int i=1;i<=m;i++){//合并长度从1到m 57 if(tem1>d[i][m+i-1]) tem1=d[i][m+i-1]; 58 if(tem2<p[i][m+i-1]) tem2=p[i][m+i-1]; 59 } 60 if(n==1) tem1=tem2=a[1]; 61 printf("%d\n%d\n",tem1,tem2); 62 } 63 return 0; 64 }