石子合并[DP-N3]

题目描述

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

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

输入输出格式

输入格式:

 

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

 

输出格式:

 

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

--------------------------------------------------------

环形DP,前缀和,O(n3)即可

可以i降序j升序,也可以枚举长度

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=105<<2,INF=1e9;
int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,a[N],mx=0,mn=INF;
int f[N][N],s[N],d[N][N];
void dp(){
    for(int i=1;i<=2*n;i++)for(int j=1;j<=2*n;j++) d[i][j]=INF,d[i][i]=0;
    
    for(int i=1;i<=2*n;i++) s[i]=s[i-1]+a[i];
//    for(int i=2*n-1;i>=1;i--)
//        for(int j=i+1;j<=2*n&&j-i<n;j++)
//            for(int k=i;k<j;k++){
//                f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]);
//                d[i][j]=min(d[i][j],d[i][k]+d[k+1][j]+s[j]-s[i-1]);
//            }
    for(int l=1;l<n;l++)
        for(int i=1;i<=2*n;i++){
            int j=min(2*n,i+l);
            for(int k=i;k<j;k++){
                f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]);
                d[i][j]=min(d[i][j],d[i][k]+d[k+1][j]+s[j]-s[i-1]);
            }
        }
    
}
int main(int argc, const char * argv[]) {
    n=read();
    for(int i=1;i<=n;i++) a[i]=read(),a[i+n]=a[i];
    dp();
    for(int i=1;i<=n;i++) mx=max(f[i][i+n-1],mx),mn=min(mn,d[i][i+n-1]);
    printf("%d\n%d",mn,mx);
    return 0;
}

 

posted @ 2016-08-31 15:18  Candy?  阅读(230)  评论(0编辑  收藏  举报