合并果子(续)

合并果子(续)

前面说过一种合并果子是任意型的http://www.cnblogs.com/jiangjun/archive/2012/11/11/2765474.html

下面来看两种类型:直线型和圆形型

一、直线型

 

问题描述
 
在一个直线上依次摆放N堆石(N≤100),现要将石子有次序地合并成一堆。规定每次只能选相邻的两堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
输入:堆数N及每堆的石子数(≤20),
选择一种合并石子的方案,使得做N-1次合并,得分的总和最小(或最大)
 
分析:和矩阵连乘类似,不能像任意型那样用贪心,那用DP求解
状态转移方程如下:
 

 

f[i][j]为第i堆石子到第j堆石子合并后的最少总分数,sum[i][j]表示第i堆石子到第j堆石子的总数量
思想大概就是这样的,代码就借用别人的,如下:
# include<stdio.h>  
#define inf 9999999
#define N 105   
int sum[N];   
int best[N][N];   
int n,stone[N]; 
int min(int a,int b)
{
    return a<b?a:b;
}
int getBest()
{   
    int i,v,j,k;
    int add;
    for(i=0;i<n;i++)//没有合并前花费为0
    {   
        best[i][i]=0;   
    }    
    for(v=1;v<n;v++)//需合并的长度 
    {     
        for(i=0;i<n-v;i++) 
        {
            j=i+v;   
            best[i][j]=inf;   
            add=sum[j]-(i>0?sum[i-1]:0);//做一点处理,但依旧是第i堆石子到第j堆石子的总数量   
            for(k=i;k<j;k++) 
            {  
                best[i][j]=min(best[i][j],best[i][k]+best[k+1][j]+add);   
            }   
        }   
    }   
    return best[0][n-1];   
}   

int main()
{   
    int i;
    int best;
    scanf("%d",&n);   
    for(i=0;i<n;i++)   
        scanf("%d",&stone[i]);   
    sum[0]=stone[0];   
    for(i=1;i<n;i++)
    {   
        sum[i]=sum[i-1]+stone[i];   
    }   
    best=getBest();   
    printf("%d\n",best);   
    return 0;   
}


二、圆形型

问题描述
 
在一个园形操场的四周摆放N堆石子(N≤100),现要将石子有次序地合并成一堆。规定每次只能选相邻的两堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
输入:堆数N及每堆的石子数(≤20),
选择一种合并石子的方案,使得做N-1次合并,得分的总和最小(或最大)
 
分析:这和上面的直线型比,需要考虑第一个和最后一个的合并,还要在原代码上作几点改变,①f(i, j)变成表示从第i个开始,合并后面j个得到的最优值;sum(i, j)变成表示从第i个开始直到i+j个的数量和  ②最后在返回值时,因为没有头,所以要全部遍历一遍
同样,DP状态转换方程如下:

f[i][j]为第i堆石子到第i+j堆石子合并后的最少总分数,sum[i][i+j]表示第i堆石子到第i+j堆石子的总数量
思想有了,代码就借用别人的,如下:
# include<stdio.h>  
#define inf 9999999
#define N 105   
int sum[N];   
int best[N][N];   
int n,stone[N]; 
int min(int a,int b)
{
    return a<b?a:b;
}
int sums(int i, int j)
{   
    if(i+j>=n)
        return sums(i,n-i-1)+sums(0,(i+j)%n);   
    else   return sum[i+j]-(i>0?sum[i-1]:0);   
}   


int getBest()
{   
    int i,j,k;
    int minnum;
    for(i=0;i<n;i++)//没有合并前花费为0
    {   
        best[i][i]=0;   
    }    
    for(j=1;j<n;j++)//需合并的长度 
    {     
        for(i=0;i<n;i++) 
        {  
            best[i][j]=inf;   
            for(k=0;k<j;k++) 
            {  
                best[i][j]=min(best[i][j],best[i][k]+best[(i+k+1)%n][j-k-1]+sums(i,j));   
            }   
        }   
    }
    minnum=best[0][n-1];
    for(i = 0;i<n;i++)//因为没有头,所以要全部遍历一遍 
    {   
        minnum=min(minnum,best[i][n-1]);   
    }
    return minnum;  
}   

int main()
{   
    int i;
    int best;
    scanf("%d",&n);   
    for(i=0;i<n;i++)   
        scanf("%d",&stone[i]);   
    sum[0]=stone[0];   
    for(i=1;i<n;i++)
    {   
        sum[i]=sum[i-1]+stone[i];   
    }   
    best=getBest();   
    printf("%d\n",best);   
    return 0;   
}

 

posted on 2012-11-15 16:33  即为将军  阅读(625)  评论(0)    收藏  举报

导航