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

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堆的石子每堆的石子数,每两个数之间用一个空格分隔。

 

输出

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

 

输入样例

4 4 4 5 9 0

 

输出样例

43

解析:这个题,,我原以为它是和矩阵连乘乘积的那个题一样,后来发现有两点不同。第一点是对于矩阵连乘的题目,num=arr[i][k]+arr[k+1][j]+row[i]*col[k]*col[j],这里一个新的arr的数是两个子项代价加起来,然后再加这两个子项算的代价,而这个摆石子是算的两个子项加起来,然后再加这两个的和。。。好吧我有点卡住了,我不知道怎么把以前的代价加进去。

像矩阵连乘那样,把对角线都设为0,算当前节点m [ i ] [ j ] 时,取其中间节点h , 然后算m[i[[j]=m[i][h]+m[h+1][j]+x,这个x为把这两个子节点连起来的代价,矩阵连乘是将 i , h , j 的维数乘起来,这里的话,应该是将当前两堆直接加起来,举个例子,如:

对于数据7,3,4,8,其产生的矩阵就应该为:

0       10      21      43            

0       0        7        22                   

0       0        0        12

0       0        0         0

上面为第一次算法后,第一次的m[i][j]就等于stone[i]+stone[j],当进行到m[1][3]和m[2][4]时,如m[1][3]=min( m[1][1]+m[2][3]+stone[1]+stone[2]+stone[3] ,m[1][2]+m[3][3]+stone[1]+stone[2]+stone[3]),m[2][4]=min( m[2][2]+m[3][4]+stone[2]+stone[3]+stone[4] ,m[2][3]+m[4][4]+stone[1]+stone[2]+stone[3] )。

事实证明,写博客有利于整理思路!啊哈哈哈,详见代码:

#include 
#include
#include
using namespace std;
int n;
int stone[100];
int m[100][100],s[100][100];//s是记录路径的
int num(int m,int n)
{
    int res=0;
    for(int i=m;i<=n;i++)
        res+=stone[i];
    return res;
}
int main()
{
    while(~scanf("%d",&n)&&n)
    {
        memset(stone,0,sizeof(stone));
        memset(m,0,sizeof(m));
        memset(s,0,sizeof(s));
        for(int i=1;i<=n;i++)
        {
            cin>>stone[i];
        }
        for(int r=1;r

不过上面这个是仿照矩阵连乘写的,本题中石子是环形的,那么那个n维矩阵每一个元素都要用到了,循环里面也需加一点东西就可以。那么对于主层循环,

for(int r=1;r         {
            for(int i=1;i<=n-r;i++)
            {
                m[i][i+r]=m[i][i]+m[i+1][i+r]+num(i,i+r);
                s[i][i+r]=i;
                for(int j=i+1;j  间隔r自然是从1到n-1不变的,对于每一个开头的 i,应该改为 i<=n 即可,算法是m[i][(i+r)%n],既可以是m[1][3],也可以是m[3][1],且这两种算法是不同的,对应的 j 应该是1 2 3和3 4 1,用统一来表示就要取余,详见代码:

#include 
#include
#include
using namespace std;
int n;
int stone[100];
int m[100][100],s[100][100];//s是记录路径的
int num(int s,int t)
{
    int res=0;
    if(s<=t)
    {
        for(int i=s;i<=t;i++)
            res+=stone[i];
    }
    else
    {
        for(int i=s;i>stone[i];
        }
        for(int r=1;r=0;j=(j+1)%n,k--)
                {
                    if(m[i][j%n]+m[(j+1)%n][(i+r)%n]+num(i,(i+r)%n)

 

 

 

 

你可能感兴趣的:(算法设计与分析)