/*
在一个圆形操场的四周摆放 N 堆石子,现要将石子有次序地合并成一堆,
规定每次只能选相邻的 2 堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出一个算法,计算出将 N 堆石子合并成 1 堆的最小得分和最大得分。

洛谷题号:P1880    https://www.luogu.com.cn/problem/P1880

*/

/*
状态转移方程
dp[i][j]:表示将第i~j对石子合并为一堆石子的最小得分
dp[i][j]= min( dp[i][k] + dp[k+1][j] )  (i <= k < j)
k是某个分界点,左边一堆 i~k 的最小得分就是dp[i][k],右边一堆 k+1~j 的最小得分是dp[k+1][j]
状态转移方程需要结合分析图来看,分析图参考同目录下的 '11 石子合并问题.png'
*/

// 这里先用递归方式来实现上述动态转移方程
#include <cstdio>
#include <iostream>
#include <cmath>
using namespace std;


int a[100] = {0};                    //前缀和,可以简化第1堆石子的总分数求和
int dp[100][100] = {0};

int stoneMerge(int l, int r) {
    //边界值

    if(l == r) return 0; //只有一堆的情况无需再分堆
    if(dp[l][r]>0) return dp[l][r];
    
    int ans = INT_MIN;
    for (int k = l; k < r; k++) {
        ans = max(ans, stoneMerge(l, k) + stoneMerge(k + 1, r));
    }

    ans += (a[r] - a[l - 1]);
    
    dp[l][r] = ans;
    return dp[l][r];
}
//l表示起始堆好,r表示末尾堆号


/*测试数据
    7
    13
    7
    8
    16
    21
    4
    18
*/
int main(void) {
    int n = 0;//输入的整数的个数
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        a[i] += a[i - 1];
    }
    int minScore = stoneMerge(1, n);
    cout << "\n" << minScore;
    return 0;
}

 

posted on 2025-04-06 16:32  FYJUN2077  阅读(36)  评论(0)    收藏  举报