石子合并优化

石子合并优化

题目描述

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

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

输入格式

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

输出格式

输出共1行,最大得分

样例

样例输入

4
4 4 5 9

样例输出

54

思路分析

  • 看似是一道裸的区间dp,但关键是数据范围,如果找板子写,n3 的效率不炸才怪嘞,优化是必须的

  • 主要问题在转移方程这里(该带佬发功了):

    • 最大值有一个性质,总是在两个端点的最大者中取到。

      • 说一下我的思路:

        区间dp是在不多扩展宽度的,宽度大的,其值一定可以做到比它区间小的值要大,这样我们在扩张区间宽度时,最大值在此基础上再次处理即可

    • 转移方程:f[i][j]=max(f[i][j-1],f[i+1][j])+sum[j]-sum[i-1]

    • 另外还有四边形不等式优化,但是现对复杂,转载一下:https://blog.csdn.net/noiau/article/details/72514812

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn=4010,INF=0x3f3f3f3f;
int n,m,f[maxn][maxn],ans,a[maxn],sum[maxn];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){    //环形就拓展长度即可
        scanf("%d",&a[i]);
        a[i+n]=a[i];
        sum[i]=a[i]+sum[i-1];
    }
    for(int i=n+1;i<=2*n;i++){
        sum[i]=sum[i-1]+a[i];
    }
    for(int d=2;d<=n;d++){ //枚举区间长度
        for(int i=1,j;(j=i+d-1)<=n*2;i++){
            f[i][j]=max(f[i][j-1],f[i+1][j])+sum[j]-sum[i-1];
        }
    }
    int ans=0;
    for(int i=1;i<=n;i++){
        ans=max(ans,f[i][i+n-1]);
    }
    printf("%d",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/hhhhalo/p/12861187.html