石子合并 (动态规划)


一.试题

在一个园形操场的四周摆放N堆石子(N≤100),现要将石子有次序地合并成一堆。规定

每次只能选相邻的两堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。

编一程序,由文件读入堆数N及每堆的石子数(≤20),

①选择一种合并石子的方案,使得做N-1次合并,得分的总和最小;

②选择一种合并石子的方案,使得做N-1次合并,得分的总和最大。

例如,所示的4堆石子,每堆石子数(从最上面的一堆数起,顺时针数)依

次为4594。则3次合并得分总和最小的方案:8+13+22=43

得分最大的方案为:14+18+22=54


</pre><pre name="code" class="cpp">#include<iostream>
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define N 105
//定义二维数组m[i][j]来记录i到j的合并过成中最少石子数目
int solve_min(int *p,int n)
{
    int i,j,k,r,sum;
    int m[N][N];
    memset(m,-1,sizeof(m));
    for(i=1; i<=n; i++)   //当一个单独合并时,m[i][i]设为0,表示没有石子
        m[i][i]=0;
    for(i=1; i<n; i++)  //当相邻的两堆石子合并时,此时的m很容易可以看出是两者之和
        m[i][i+1]=p[i]+p[i+1];
    for(r=3; r<=n; r++) //当相邻的3堆以及到最后的n堆时,执行以下循环
    {
        for(i=1; i<=n-r+1; i++)
        {
            j=i+r-1;
            sum=0;
            for(k=i; k<=j; k++)   //当i到j堆石子合并时最后里面的石子数求和得sum
                sum+=p[k];
            m[i][j]=m[i+1][j]+sum;
// 此时m[i][j]为i~j堆石子间以m[i][i]+m[i+1][j]+sum结果,这是其中一种可能,不一定是最优
            for(k=i+1; k<j; k++)
            {
                int t=m[i][k]+m[k+1][j]+sum;
                if(t<m[i][j])
                    m[i][j]=t;
            }
        }
    }
    return m[1][n];
}
int solve_max(int *p,int n)
{
    int i,j,k,r,sum;
    int m[N][N];
    memset(m,-1,sizeof(m));
    for(i=1; i<=n; i++)
        m[i][i]=0;
    for(i=1; i<n; i++)
        m[i][i+1]=p[i]+p[i+1];
    for(r=3; r<=n; r++)
    {
        for(i=1; i<=n-r+1; i++)
        {
            j=i+r-1;
            sum=0;
            for(k=i; k<=j; k++)
                sum+=p[k];
            m[i][j]=m[i+1][j]+sum;
            for(k=i+1; k<j; k++)
            {
                int t=m[i][k]+m[k+1][j]+sum;
                if(t>m[i][j])
                    m[i][j]=t;
            }
        }
    }
    return m[1][n];
}
int main()
{
    int i,j,k,n,stone[N];
    while(scanf("%d",&n)!=-1)
    {
        for(i=1; i<=n; i++)
        {
            scanf("%d",&stone[i]);
        }
        int mmin=solve_min(stone,n);
        int mmax=solve_max(stone,n);
        for(j=1; j<n; j++)
        {
            int t=stone[1];
            for(k=1; k<n; k++)
            {
                stone[k]=stone[k+1];
            }
            stone[n]=t;
            int tmin=solve_min(stone,n);
            int tmax=solve_max(stone,n);
            if(tmin<mmin)
                mmin=tmin;
            if(tmax>mmax)
                mmax=tmax;
        }
        printf("%d\n%d\n",mmin,mmax);
    }
    return 0;
}
时间: 2024-10-01 18:53:17

石子合并 (动态规划)的相关文章

动态规划思想:石子合并问题

描述:在一个圆形操场的四周摆放着n 堆石子.现要将石子有次序地合并成一堆.规定每次只能选相邻的2 堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分.试设计一个算法,计算出将n堆石子合并成一堆的最小得分和最大得分. 贪心算法不能得到最优解,可惜了.首先我们可以把这么堆石子看成一列 如果N-1次合并的全局最优解包含了每一次合并的子问题的最优解,那么经这样的N-1次合并后的得分总和必然是最优的.因此我们需要通过动态规划算法来求出最优解. 在此我们假设有n堆石子,一字排开,合并相邻两堆的石子,

zjnu1181 石子合并【基础算法?动态规划】——高级

Description 在操场上沿一直线排列着 n堆石子.现要将石子有次序地合并成一堆.规定每次只能选相邻的两堆石子合并成新的一堆, 并将新的一堆石子数记为该次合并的得分.允许在第一次合并前对调一次相邻两堆石子的次序. 计算在上述条件下将n堆石子合并成一堆的最小得分. Input 输入数据共有二行,其中,第1行是石子堆数n≤100: 第2行是顺序排列的各堆石子数(≤20),每两个数之间用空格分隔. Output 输出合并的最小得分. Sample Input 3 2 5 1 Sample Out

算法温习动态规划之石子合并问题

石子合并问题分为直线型和圆形: 直线型: 直线型狮子合并问题存在以下递推式: f[i][j]:表示从第i堆合并到底j堆,最少代价 f[i][j]=0;     i=j f[i][j]=min( f[i][k]+f[k+1][j]+sum(i,j));       i<=k<j; 这个问题比较好理解,根据递推式,我们的i要从高到底遍历,j要从低到高遍历 直线型代码如下: #include<iostream> #include<vector> using namespace

合并类动态规划——石子合并

题目描述 Description 在一个园形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分.试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分. 输入输出格式 Input/output 输入格式:数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.输出格式:输出共2行,第1行为最小得分,第2行为最大得分. 输入输出样例 Sample input/out

石子合并的动态规划问题

题目大概都是这样的: 设有N堆沙子排成一排,其编号为1,2,3,-,N(N<=300).每堆沙子有一定的数量,可以用一个整数来描述,现在要将这N堆沙子合并成为一堆,每次只能合并相邻的两堆,合并的代价为这两堆沙子的数量之和,合并后与这两堆沙子相邻的沙子将和新堆相邻,合并时由于选择的顺序不同,合并的总代价也不相同,如有4堆沙子分别为 1  3  5  2 我们可以先合并1.2堆,代价为4,得到4 5 2 又合并 1,2堆,代价为9,得到9 2 ,再合并得到11,总代价为4+9+11=24,如果第二步

动态规划—石子合并(直线和环)

先来看直线的: N堆石子摆成一条线.现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的代价.计算将N堆石子合并成一堆的最小代价. 例如: 1 2 3 4,有不少合并方法 1 2 3 4 => 3 3 4(3) => 6 4(9) => 10(19) 1 2 3 4 => 1 5 4(5) => 1 9(14) => 10(24) 1 2 3 4 => 1 2 7(7) => 3 7(10) =>

四边形不等式优化DP——石子合并问题 学习笔记

好方啊马上就要区域赛了连DP都不会QAQ 毛子青<动态规划算法的优化技巧>论文里面提到了一类问题:石子合并. n堆石子.现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分. 求出将n堆石子合并成一堆的最小得分和最大得分以及相应的合并方案. 设m[i,j]表示合并d[i..j]所得到的最小得分. 状态转移方程: 总的时间复杂度为O(n3). [优化方案] 四边形不等式: m[i,j]满足四边形不等式 令s[i,j]=max{k | m[

luogu P1880 石子合并

题目描述 在一个园形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分. 试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分. 输入输出格式 输入格式: 数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数. 输出格式: 输出共2行,第1行为最小得分,第2行为最大得分. 输入输出样例 输入样例#1: 4 4 5 9 4 输出样例#1: 43 54 区间动态规

[NYIST737]石子合并(一)(区间dp)

题目链接:http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=737 很经典的区间dp,发现没有写过题解.最近被hihocoder上几道比赛题难住了,特此再回头重新理解一遍区间dp. 这道题的题意很明确,有一列石子堆,每堆石子都有数量,还有一个操作:相邻两堆石子合并成一堆石子,这个操作的代价是这两堆石子的数目和.要找一个合并次序,使得代价最小,最终输出最小代价. 这个题可以用动态规划,简单分析可以得知,这一列石子堆都可以划分为小区间,每个小区间