【51Nod 1022】石子归并 V2

Description

1022 石子归并 V2
基准时间限制:1 秒 空间限制:131072 KB 分值: 160 难度:6级算法题
N堆石子摆成一个环。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的代价。计算将N堆石子合并成一堆的最小代价。

例如: 1 2 3 4,有不少合并方法
1 2 3 4 => 3 3 4(3) => 6 4(9) => 10(19)
1 2 3 4 => 1 5 4(5) => 1 9(14) => 10(24)
1 2 3 4 => 1 2 7(7) => 3 7(10) => 10(20)

括号里面为总代价可以看出,第一种方法的代价最低,现在给出n堆石子的数量,计算最小合并代价。
Input
第1行:N(2 <= N <= 1000)
第2 - N + 1:N堆石子的数量(1 <= A[i] <= 10000)
Output
输出最小合并代价
Input示例
4
1
2
3
4
Output示例
19

Solution

普通的DP很容易就能够想到: fi,j 表示从第 i 堆石子合并到第 j 颗石子的最小代价是什么,只要枚举间接点 k 就能够轻松转移。但是发现一个很重要的问题,时间高达 n3 ,问题就在于如何优化 k 的枚举。
这里就要引进一种DP优化方式:四边形不等式优化。设a<b<c<d,若 fa,c+fb,dfb,c+fa,d 则满足四边形不等式。
这道题设 gi,j 表示对应 i,j 最优状态的 k 值,可以发现 gi,j 是满足四边形不等式的。
1、 gi,1gi,2......gi,n1gi,n
2、 g1,ig2,i......gn1,ign,i
稍微转化一下就能够弄出关于 gi,j 的关系式: gi,j1gi,jgi+1,j ,因为所有 k 的枚举个数和是n,所以总时间复杂度是 n2

Code

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
int f[2001][2001],n,i,j,k,x,g[2001][2001],a[1001],s[2001],ans;
int main(){
    scanf("%d",&n);
    s[0]=0;
    fo(i,1,n){
        scanf("%d",&a[i]);s[i]=s[i-1]+a[i];
    }
    fo(i,1,n){
        s[i+n]=s[i+n-1]+a[i];
    }
    memset(f,127,sizeof(f));
    fo(i,1,2*n){
        g[i][i]=i;
        f[i][i]=0;
    }
    fo(x,1,n){
        fo(i,1,n*2-1){
            j=i+x;
            if(j>n*2-1)break;
            fo(k,g[i][j-1],g[i+1][j])
                if(f[i][k]+f[k+1][j]+s[j]-s[i-1]
                    f[i][j]=f[i][k]+f[k+1][j]+s[j]-s[i-1];
                    g[i][j]=k;
                }
        }
    }
    ans=0x7777777;
    fo(i,1,n)ans=min(ans,f[i][i+n-1]);
    printf("%d",ans);
}

你可能感兴趣的:(DP,51Nod)