不能移动的石子合并

Description

做如下两个模型的石子合并,如下模型石子都不能移动出列,且合并都仅发生在相邻两堆石子中:

 

(1)第一个模型:一行排列且相邻合并

有n堆石子A1,A2,...,An形成一行,每堆石头个数记为ai(1<=i<=n),相邻两堆可合并,合并的分值为新堆的

石子数。求合并为一堆的最低得分和最高得分。

 

(2)第二个模型:一圈排列且相邻合并

有n堆石子A1,A2,...,An形成首位相连的一个环形,An和A1相邻,每堆石头个数记为ai(1<=i<=n),相邻两堆

可合并,合并的分值为新堆的石子数。求合并为一堆的最低得分和最高得分。

 

例如4堆石子,每堆石子个数:9 4 4 5

若排成一行,最小分值:(4+4)+(8+5)+(9+13)=43,最大分值:(9+4)+(13+4)+(17+5)=52。

若排成圈状,最小分值:(4+4)+(8+5)+(9+13)=43,最大分值:(9+5)+(14+4)+(18+4)=54。

 

此题以第一模型的最低得分为例,很多同学想着采用总是从最小的相邻两堆下手的思想,认为最后获得的也就是最

低得分。但这个贪心策略是不对的。如下反例:

 

石子:9 4 6 1 5

 

贪心策略:

9 4 6 6      计分6

9 10 6       计分10

9 16         计分16

25           计分25

得分共计:6+10+16+25=57

 

但9 4 6 1 5 若如下方式合并:

13 6 1 5     计分13

13 6 6       计分6

13 12        计分12

25           计分25

13+6+12+25=56

 

9 4 6 6      计分6

9 4 12       计分12

13 12        计分13

25           计分25

6+12+13+25=56

 

后两种方式合并出的56都比贪心策略的57来的更低,因为总选择最小的相邻两堆去合并,并不能保证后续每步

都可以最小,也许这轮最小导致后续几轮分值较大。


输入格式

两行。第一行n,第二行a1 a2 … an,每个ai(1<=i<=n)表示第i堆石子的个数,n<=100

 

输出格式

两行。第一行是第一个模型的最低得分和最高得分,中间空格相连,第二行是第二个模型的最低得分和

最高得分,中间空格相连。

 

输入样例

4

9 4 4 5

 

输出样例

43 52

43 54

(1)第一个石子合并模型

 

和书上3.1节的矩阵连乘问题类似。假设m[i,j]为合并石子ai…aj,1<=i<=j<=n。所得到的最小

得分,若没有“合并”这个动作,则为0。

原问题所求的合并最小值即为m[1,n]。

 

递推公式如下,其中min表示求最小,sum表示求和。

    m[i,j] = 0,   if i=j

    m[i,j] = min{ m[i,k]+m[k+1,j] | for all k, i<=k<j } + sum{ a(t) | for all t, i<=t<=j },

                  if i<j

 

至于求最大值完全同理,自行推导递归公式。

 

 

(2)第二个石子合并的环行模型

 

方法一、环型模型完全可以转化为行型模型来求解。

环形模型可视为等价的行形模型: A1 ... An A1 ... An-1,共2n-1堆,并在此行形模型中合并石头不超过

链长n。

环行模型问题所求,不是等价模型所填充的右上方元素m[1,2n-1],而是m[1,n], m[2,n+1], …, m[n,2n-1]

这n个斜行元素中的最小值,

即: min{ m[i, n+i-1] | for all i, 1<=i<=n }

 

 

方法二、定义链,但环型定义链如果和前面行型一样,用链的起始和结束下标的话,不好定义,我们得换一

种定义方式,加入链长这个参数而去掉结束下标这个参数。

 

定义环行的链p(i,r),表示石头堆链Ai, A[(i+1)%n], ..., A[(i+r-1)%n]。  那链p(i,r)能够合并的最小分

值记为m(i,r)。这里,1<=i<=n, 1<=r<=n。

 

Ai  ...  A[(i+s-1)%n]    A[(i+s)%n]  ...  A[(i+r-1)%n]

---------------------    -----------------------------

      s个(链长s)                r-s个(链长r-s)

 

最后一次合并在A[(i+s-1)%n]和A[(i+s)%n]之间,分为两个子链,左一个子链是p(i,s),右一个子链是p((i+s)%n, r-s)。

链p(i,r)的最优值由两个子链的最优值得到。

 

   m(i,r) = 0     r=1;

   m(i,r) = min{ m(i,s)+m((i+s)%n, r-s) | for all s, 1<=s<r} + sum{a(t) | for all t, i<=t<=(i+r-1)%n }

               1<r<=n

 

由链长增加的方向来填充m(i,r)数组,原问题所求为: max{m(i,n) | 1<=i<=n}

 

例如: n=5,  A1 A2 A3 A4 A5 构成环行。

计算:

r=1, m[i,1] (1<=i<=n), 即:m[1,1], m[2,1], m[3,1], m[4,1], m[5,1]。 

对应的矩阵链为:A1, A2, A3, A4, A5;

r=2, m[i,2] (1<=i<=n), 即:m[1,2], m[2,2], m[3,2], m[4,2], m[5,2]。 

对应的矩阵链为:A1A2, A2A3, A3A4, A4A5, A5A1;

r=3, m[i,3] (1<=i<=n), 即:m[1,3], m[2,3], m[3,3], m[4,3], m[5,3]。 

对应的矩阵链为:A1A2A3, A2A3A4, A3A4A5, A4A5A1, A5A1A2;

......

r=n, m[i,n] (1<=i<=n), 即:m[1,n], m[2,n], m[3,n], m[4,n], m[5,n]。 

对应的矩阵链为:A1A2...An, A2A3...A1, A3A4...A2, A4A5...A3, A5A1...A2;

 

对m二维数组一列一列填充,原问题所求环行的最小得分为最后填充的一列m元素:

m[1,n], m[2,n], m[3,n], m[4,n], m[5,n]的最大值,即 max{m(i,n) | 1<=i<=n}。

#include <cstdio>

using namespace std;

int min_score(int n, int *a);
int max_score(int n, int *a);
int min_score2(int n, int *a);
int max_score2(int n, int *a);

int main()
{
    int n;
    scanf("%d", &n);
    int *a = new int[n + 1];
    for(int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
    }
    int min1 = min_score(n, a);
    int max1 = max_score(n, a);

    printf("%d %d\n", min1, max1);


    int min2 = min_score2(n, a);
    int max2 = max_score2(n, a);

    printf("%d %d\n", min2, max2);

    return 0;
}

int sum(int m, int n, int *a)
{
    int sum1 = 0;
    for(int i = m; i <= n; i++)
        sum1 += a[i];
    return sum1;
}

int min_score(int n, int *a)
{
    int **m = new int*[n + 1];
    for(int i = 0; i <= n; i++)
    {
        m[i] = new int[n + 1];
    }
    for(int i = 0; i <= n; i++) m[i][i] = 0;

    for(int r = 2; r <= n; r++)
    {
        for(int i = 1; i <= n - r + 1; i++)
        {
            int j = i + r - 1;
            int sum1 = sum(i, j, a);
            m[i][j] = m[i + 1][j] + sum1;
            for(int k = i + 1; k < j; k++)
            {
                int t = m[i][k] + m[k + 1][j] + sum1;
                if(t < m[i][j]) m[i][j] = t;
            }
        }
    }

    return m[1][n];
}

int max_score(int n, int *a)
{
    int **m = new int*[n + 1];
    for(int i = 0; i <= n; i++)
    {
        m[i] = new int[n + 1];
    }
    for(int i = 0; i <= n; i++) m[i][i] = 0;

    for(int r = 2; r <= n; r++)
    {
        for(int i = 1; i <= n - r + 1; i++)
        {
            int j = i + r - 1;
            int sum1 = sum(i, j, a);
            m[i][j] = m[i + 1][j] + sum1;
            for(int k = i + 1; k < j; k++)
            {
                int t = m[i][k] + m[k + 1][j] + sum1;
                if(t > m[i][j]) m[i][j] = t;
            }
        }
    }

    return m[1][n];
}

int sum2(int m, int len, int n, int *a)
{
    int sum1 = 0;
    a[0] = a[n];
    for(int i = 1; i <= len; i++)
        sum1 += a[(m + i - 1) % n];
    return sum1;
}

int min_score2(int n, int *a)
{
    int **m = new int*[2 * n];
    for(int i = 0; i < 2 * n; i++)
    {
        m[i] = new int[2 * n];
    }
    for(int i = 0; i < 2 * n; i++) m[i][i] = 0;

    for(int r = 2; r <= n; r++)
    {
        for(int i = 1; i <= 2 * n - r; i++)
        {
            int j = i + r - 1;
            int sum1 = sum2(i, r, n, a);
            m[i][j] = m[i + 1][j] + sum1;
            for(int k = i + 1; k < j; k++)
            {
                int t = m[i][k] + m[k + 1][j] + sum1;
                if(t < m[i][j]) m[i][j] = t;
            }
        }
    }
    int minone = 99999;
    for(int i = 1; i <= n; i++){
        if(m[i][i + n - 1] < minone) minone = m[i][i + n - 1];
    }

    return minone;
}

int max_score2(int n, int *a)
{
    int **m = new int*[2 * n];
    for(int i = 0; i < 2 * n; i++)
    {
        m[i] = new int[2 * n];
    }
    for(int i = 0; i < 2 * n; i++) m[i][i] = 0;

    for(int r = 2; r <= n; r++)
    {
        for(int i = 1; i <= 2 * n - r; i++)
        {
            int j = i + r - 1;
            int sum1 = sum2(i, r, n, a);
            m[i][j] = m[i + 1][j] + sum1;
            for(int k = i + 1; k < j; k++)
            {
                int t = m[i][k] + m[k + 1][j] + sum1;
                if(t > m[i][j]) m[i][j] = t;
            }
        }
    }
    int maxone = -1;
    for(int i = 1; i <= n; i++){
        if(m[i][i + n - 1] > maxone) maxone = m[i][i + n - 1];
    }

    return maxone;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值