NK1137 石子合并

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

编程任务:
对于给定n堆石子,编程计算合并成一堆的最小得分和最大得分。

Input

输入包括多组测试数据,每组测试数据包括两行。

第1 行是正整数n,1<=n<=100,表示有n堆石子。
第2行有n个数,分别表示每堆石子的个数。

Output

对于每组输入数据,输出两行。

第1 行中的数是最小得分;第2 行中的数是最大得分。

Sample Input

4
4 4 5 9

Sample Output

43
54

思路:

1、因为石子围成了一圈,所以先要将n堆的石子复制到第n+1~第2*n堆,这样才能保证无论从哪一堆开始合并,都能取到任意长度的石子堆。
2、dp[i][j] 表示从第i堆开始合并长度为j堆的石子的最优情况,这样可以求得从任意堆开始长度为x的石子堆的最优情况;
3、sum[i]表示从i开始的长度为x(变长)的石子堆所有石子总数。
4、dp[i][j] =  max { dp[i][k] ,  dp[i+k][j-k]  } + sum[i];  ( 1<=k<=n )
     但是,必须注意此处的dp[i+k][j-k],若循环只到n,可能会越界即可能取到没有算过的值,例如当n=5时,dp[5][4] = max(或min){ dp[5][1] , dp[6][3] } + sum[5]; 此处的dp[6][3]就没有被赋过值,导致“越界”。由于此处的dp[6][3]实际上相当于第1、2、3堆石子合并的最优解,即dp[1][3]。所以当(i+k)%n!=0时,dp[(i+k)%n][j+k] = dp[i+k][j-k];
    所以最后: dp[i][j] = max(或min) { dp[i][k] , dp[(i+k)%n][j-k] } + sum[i];
5、还有个要注意的长度为1的石子合并的得分一定为0。

代码:
#include<iostream>
#include<string.h>
#include<stdio.h>
#define INF 0x3ffffff
using namespace std;
int main()
{
    int a[220];
    int dp1[110][110],dp2[110][110];
    int sum[110];
    int n,ans1,ans2;
    int i,j,k;
    while(scanf("%d",&n)!=EOF)
    {
        for(i=1;i<=n;i++) scanf("%d",&a[i]);
      //  if(n==1) { printf("0\n0\n"); continue; }
        for(int i=1;i<=n;i++)  a[n+i]=a[i];
        for(i=1;i<=n;i++)
        {
            sum[i]=a[i]+a[i+1];
            for(j=1;j<=n;j++)
            {
                if(j==2) dp1[i][j]=dp2[i][j]=a[i]+a[i+1];
                else  dp1[i][j]=INF, dp2[i][j]=-INF;
            }
            dp1[i][1]=dp2[i][1]=0;
        }
        ans1=INF; ans2=-INF;
        for(j=3;j<=n;j++)
        {
            for(i=1;i<=n;i++)
            {
                sum[i]+=a[i+j-1];
                int t1,t2;
                for(k=1;k<j;k++)
                {
                    if((i+k)%n!=0)
                    t1=dp1[i][k]+dp1[(i+k)%n][j-k]+sum[i],
                    t2=dp2[i][k]+dp2[(i+k)%n][j-k]+sum[i];
                    else
                    t1=dp1[i][k]+dp1[i+k][j-k]+sum[i],
                    t2=dp2[i][k]+dp2[i+k][j-k]+sum[i];
                    dp1[i][j]=min(t1,dp1[i][j]);
                    dp2[i][j]=max(t2,dp2[i][j]);
                }
            }
        }
        for(i=1;i<=n;i++)
        {
            ans1=min(dp1[i][n],ans1);
            ans2=max(dp2[i][n],ans2);
        }
        printf("%d\n%d\n",ans1,ans2);
    }
    return 0;
}



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值