石子合并问题

石子合并问题

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

Hint

Source

 一,建立最优值递归式 

  • 设 Min [i][j] 代表从第 i 堆石子到第 j 堆石子合并的最小花费, Min [i][k] 代表从第 i 堆石子到第 k 堆石子合并的最小花费,Min[k+1][j] 代表从第 k+1 堆石子到第 j 堆石子合并的最小花费, w ( i , j )代表从 i 堆到 j 堆的石子数量之和。列出递归式:
  • Min [ i ][ j ] = 0 (i = j)
  • Min [ i ][ j ] = min ( Min [ i ][ k ] + Min [ k + 1][ j ] + w ( i , j )) , i < j( i ≤ k < j)
  • Max [i][j] 代表从第 i 堆石子到第 j 堆石子合并的最大花费,Max [i][k] 代表从第 i 堆
  • 石子到第 k 堆石子合并的最大花费,Max [k+1][j] 代表从第 k+1 堆石子到第 j 堆石子合并的最大花费, w ( i , j )代表从 i 堆到 j 堆的石子数量之和。列出递归式:
  • Max [ i ][ j ] = 0 (i = j)
  • Max [ i ][ j ] =max( Max [ i ][ k ] + Max [ k + 1][ j ] + w ( i , j )) , i < j ( i ≤ k < j)
  • 二维数组 Min [i][j] 、Max [i][j] 来记录第 i 堆到第 j 堆 a i , a i+1 ,..., a i 堆石子合并的最小花费和最大花费。

 

  • ( 2 )初始化
  • 输入石子的堆数 n ,然后依次输入各堆石子的数量存储在 a[i] 中,令 Min [i][i]=0 ,
  • Max [i][i]=0 , sum[0]=0 ,计算 sum[i] ,其中 i= 1 , 2 , 3 ,..., n 。
  • ( 3 )循环阶段
  • • 按照递归式计算 2 堆石子合并 {a i , a i+1 } 的最小花费和最大花费, i=1 , 2 , 3 ,..., n−1 。
  • • 按照递归式计算 3 堆石子合并 {a i , a i+1 , a i+2 } 的最小花费和最大花费, i=1 , 2 , 3 ,...,n−2 。
  • 以此类推,直到求出所有堆 {a 1 ,..., a n } 的最小花费和最大花费

样例:

  • 4 4 5 9 score: 0
  • 8 5 9 score: 8
  • 13 9 score: 8 + 13 = 21
  • 22 score: 8 + 13 + 22 = 43 

问题解析 :

  • 这个问题和直线型的区别在于最后一堆和第一堆也是相邻的,
  • 可以把圆形转换成直线型,把问题扩展为2n-1堆石子,举个例子,
  • 如果环形石子堆是4 4 5 9,那么转换成直线型就变成了 4 4 5 9 4 4 5
  • 所以最终就不是计算  0~n-1了,
  • 而是在 0~n-1,1-n,2-n+1,...,n-1~2n-2中选择最小的。计算方法和直线型的相同。

  • 对于环形的处理典型的方法是将环拆成链,也就是将长度扩大2倍,
  • 求链的最佳得分可以通过反向枚举左端点,然后找到当前到右端点的最佳,
  • 因为链的长度为原来的两倍,所以应该从2*n-1枚举,每次枚举n的长度,然后从1--n找最后的答案即可。 
#include<bits/stdc++.h>
using namespace std;
const int maxn=610;
const int inf = 0x3f3f3f;
int n,m,a[maxn],sum[maxn];
int maxx[maxn][maxn],minn[maxn][maxn];
int main()
{
    scanf("%d",&n);
    for(int i=1; i<=n; i++)
    {
        scanf("%d",&a[i]);
        sum[i]=sum[i-1]+a[i];
    }
    for(int i=n+1; i<=n*2; i++)
    {
        a[i]=a[i-n];
        sum[i]=sum[i-1]+a[i];
    }
    for(int i=n*2-1; i>=1; i--)//对2*n-1个数进行操作
    {
        for(int j=i+1; j<=i+n; j++)//每次枚举n的长度
        {
            minn[i][j]=inf;
            for(int k=i; k<=j-1; k++)//每次一个单位进行更新
            {
                minn[i][j]=min(minn[i][j],minn[i][k]+minn[k+1][j]+sum[j]-sum[i-1]);
                maxx[i][j]=max(maxx[i][j],maxx[i][k]+maxx[k+1][j]+sum[j]-sum[i-1]);
                //printf("%d %d %d %d %d %d\n",i,j,sum[j]-sum[i-1],sum[j],sum[i-1],maxx[i][j]);
            }
            //break;

        }
        //printf("\n\n");
    }
    int ans_min = inf;
    int ans_max = 0;
    for(int i=1; i<=n; i++)//在长度为n的进行找答案,区间(1-n,2-n+1,3-n+1,,,n-2*n)
    {
        ans_max=max(maxx[i][i+n-1],ans_max);
        ans_min=min(minn[i][i+n-1],ans_min);
    }
    printf("%d\n%d",ans_min,ans_max);
    return 0;

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值