【NOJ1148】【算法实验四】【DP_动态规划】石子合并


1148.石子合并

时限:1000ms 内存限制:10000K  总时限:3000ms

描述

在一个圆形(圆形!!!圆形!圆形!)操场的四周摆放着n堆石子(n<= 100),现要将石子有次序地合并成一堆。规定每次只能选取相邻的两堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。编一程序,读入石子堆数n及每堆的石子数(<=20)。选择一种合并石子的方案,使得做n-1次合并,得分的总和最小; 比如有4堆石子:4 4 5 9 则最佳合并方案如下:
4 4 5 9 score: 0
8 5 9 score: 8
13 9 score: 8 + 13 = 21
22 score: 8 + 13 + 22 = 43

输入

可能有多组测试数据。 当输入n=0时结束! 第一行为石子堆数n(1<=n<=100); 第二行为n堆的石子每堆的石子数,每两个数之间用一个空格分隔。

输出

合并的最小得分,每个结果一行。


#include 

using namespace std;

int n;

int stone[101];

int dp();

int memo[101][101];

int num(int i, int j);  //返回第i堆石子累加到第j堆石子的累加和

int main()
{
    while(cin>>n&&n)
    {
        for(int i=0; i>stone[i];
        }

        if(n==1)
        {
            cout<<'0'<

【后记】

1.刚开始写的时候以为和矩阵连乘积那道题一模一样,于是美滋滋地,张艺兴的小歌儿听着,键盘劈里啪啦的敲着,敲码一时爽,WA火葬场。。。

附上矩阵连乘积的题,是本题的简化版:https://blog.csdn.net/qq_41727666/article/details/83181232

2.然后才发现这石子踏马的是环形摆着的,不过略一思索,发现只需要扩大for循环的遍历范围,不管越不越界,多套几个(…)%n就好了。在矩阵连乘积那道题里我还讲:备忘录表的下三角用不上、不写了,结果这道题就用上了。。。

照例上图:

图中写的就是dp函数的工作过程,先初始化,再填表,建议配合代码食用。

 

本题中递推公式如下,其中k 为从第 i 堆石子开始数的第k个断开位置,范围是[0, len-1]:

memo[i][j]=min\limits_{k=0}^{k<len}(memo[(i+k+1)\%n][j]+num(i, j));

 

我们在下图的右边填表过程中可以看到(注意标绿色的数字),每次len++后,新的循环都需要填两个斜行的表,而 i 的范围总是[0, n-1],j 的范围总是[ (i+len)%n, (i+len+n-1)%n];

下图中可以看出,最后的答案有n个,相当于把这个环从n个位置断开,当作一条线(像矩阵连乘积那道题似的)分别求解而得到的n个最优解,在这些解中选一个代价最小的,就是最终真正的最优解。

【NOJ1148】【算法实验四】【DP_动态规划】石子合并_第1张图片 memo意义、初始化、填表示意图

 

3.本题中还有一个需要注意的地方:单独处理只有一堆石子(n==1)的情况,此时代价为0(无法合并)。


【2018/11/16第二版代码】

emmm第一版代码太难了导致自己无法复现orz,下面是超简单版(手动遍历每个断开位置。。。)

#include 
using namespace std;

int n;
int stone[101];
int memo[101][101];

int dp(int m, int n);
int store(int m, int n);

int main()
{
    while(cin>>n&&n)
    {
        for(int i=0; i>stone[i];
        }

        int minnum=INT_MAX;
        for(int i=0; i

 

你可能感兴趣的:(#,算法设计NOJ)