石子合并问题(环形)

石子合并问题

Time Limit: 1000 ms Memory Limit: 65536 KiB

Submit Statistic

Problem Description

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

Input

输入数据的第1行是正整数n,1≤n≤100,表示有n堆石子。第二行有n个数,分别表示每堆石子的个数。

Output

输出数据有两行,第1行中的数是最小得分,第2行中的数是最大得分。

Sample Input

4
4 4 5 9

Sample Output

43
54
#include <bits/stdc++.h>
using namespace std;
int arr[205];
int dp_1[205][205],dp_2[205][205];
int sum[205];
int main()
{
    int n,ans_min = 2e9,ans_max = -1,j;
    sum[0] = 0;
    cin>>n;
    for(int i = 1; i <= n; i++)
    {
        cin>>arr[i];
        sum[i] = sum[i-1] + arr[i];
    }
    //通过取余操作成环
    for(int i = n+1; i <= 2*n; i++)
    {
        sum[i] = sum[i-1] + arr[i%n];
    }
    memset(dp_1,0,sizeof(dp_1));
    memset(dp_2,0,sizeof(dp_2));
    for(int m = 1; m < n; m++)
    {
        for(int i = 1; i < 2*n; i++)
        {
            j = i + m;
            if(j >= 2*n)
                break;
            dp_2[i][j] = 2e9;
            for(int k = i; k < j; k++)
            {
                dp_1[i][j] = max(dp_1[i][j],dp_1[i][k]+dp_1[k+1][j] + sum[j]-sum[i-1]);
                dp_2[i][j] = min(dp_2[i][j],dp_2[i][k]+dp_2[k+1][j] + sum[j]-sum[i-1]);
            }
        }
    }
    for(int i = 1; i < 2*n; i++)
    {
        j = i + n-1;
        if(j >= 2*n) break;
        ans_max = max(ans_max,dp_1[i][j]);
        ans_min = min(ans_min,dp_2[i][j]);
    }
    cout<<ans_min<<endl;
    cout<<ans_max<<endl;
    return 0;
}

 

环形石子合并问题是要将圆形操场四周的`n`石子有序合并一堆每次只能合并相邻两堆,且将新石子数记为该次合并得分,需计算出最小得分和最大得分。用C语言解决此问题可借助动态规划算法,同时结合环形处理与前缀和技术 [^1][^2]。 ### 解决思路 1. **环形处理**:把原数组复制一份接在后面,从而将环形排列转化为线性排列。 2. **前缀和**:构建前缀和数组,以此加速区间和的计算,减少重复计算。 3. **动态规划**:运用动态规划来计算最小得分和最大得分。 ### 代码示例 ```c #include <stdio.h> #include <string.h> #define MAXN 205 #define INF 0x3f3f3f3f int a[MAXN]; int sum[MAXN]; int dp_min[MAXN][MAXN]; int dp_max[MAXN][MAXN]; int main() { int n; scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); a[i + n] = a[i]; // 复制数组处理环形 } // 计算前缀和 for (int i = 1; i <= 2 * n; i++) { sum[i] = sum[i - 1] + a[i]; } // 初始化dp数组 memset(dp_min, INF, sizeof(dp_min)); memset(dp_max, 0, sizeof(dp_max)); for (int i = 1; i <= 2 * n; i++) { dp_min[i][i] = 0; dp_max[i][i] = 0; } // 动态规划计算 for (int len = 2; len <= n; len++) { for (int i = 1; i <= 2 * n - len + 1; i++) { int j = i + len - 1; for (int k = i; k < j; k++) { dp_min[i][j] = (dp_min[i][j] < dp_min[i][k] + dp_min[k + 1][j] + sum[j] - sum[i - 1]) ? dp_min[i][j] : dp_min[i][k] + dp_min[k + 1][j] + sum[j] - sum[i - 1]; dp_max[i][j] = (dp_max[i][j] > dp_max[i][k] + dp_max[k + 1][j] + sum[j] - sum[i - 1]) ? dp_max[i][j] : dp_max[i][k] + dp_max[k + 1][j] + sum[j] - sum[i - 1]; } } } // 找出最小和最大得分 int ans_min = INF; int ans_max = 0; for (int i = 1; i <= n; i++) { ans_min = (ans_min < dp_min[i][i + n - 1]) ? ans_min : dp_min[i][i + n - 1]; ans_max = (ans_max > dp_max[i][i + n - 1]) ? ans_max : dp_max[i][i + n - 1]; } printf("最小得分: %d\n", ans_min); printf("最大得分: %d\n", ans_max); return 0; } ``` ### 代码解释 1. **环形处理**:`a[i + n] = a[i]`把原数组复制一份接在后面。 2. **前缀和**:`sum[i] = sum[i - 1] + a[i]`用于计算前缀和。 3. **动态规划**:借助三重循环来更新`dp_min`和`dp_max`数组,最终找出最小得分和最大得分。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值