1.问题描述
在一个圆形操场的四周摆放着n 堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的2 堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。试设计一个算法,计算出将n堆石子合并成一堆的最小得分和最大得分。
2.动态规划or贪心算法
动态规划:用来求解最优性质的解,将原问题划分为若干个子问题,先求解子问题的解,由子问题的解求出原问题的解。这些子问题往往不互相独立,所以我们用一个表(在具体代码中经常用数组)来存储子问题的解避免重复求解。动态规划需要满足的条件为最优子结构性质即问题的最优解所包含的子问题的解也是最优的。同时需要满足子问题的重叠性和无后效性即某状态以后的过程不会影响以前的状态,只与当前状态有关。
贪心算法:同样用来求解具有最优性质的解,总是做出当前状态下的最优选择。希望得到的最终结果也是最优的。贪心算法求解最优解的条件有两个:贪心选择性质——问题的最优解可以通过局部最优解得到;最优子结构性质。
动态规划和贪心算法的主要区别在于动态规划采用自底向上的方式求解问题,贪心算法则是采用自顶向下的方法进行求解,以迭代方式继续做出贪心选择,将问题规模变为规模更小的自问题。
言归正传,石子合并问题是否可以采用贪心算法?以求得最小合并数为例,如果采用贪心算法,每次做的贪心选择为总是合并两个相邻的石子个数最小的两堆。假如有五堆石子数分别为:8 7 6 8 。
第一次合并:8 7 6 8 。得分13
第二次合并:8 13 8 。得分13+ 21
第三次合并:21 8。得分13+21+29
不采用贪心算法:
第一次合并:8 7 6 8。得分15
第二次合并:15 6 8。得分15+14
第三次合并:15 14。得分15+14+29.(PS 最后一次得分总是石子总数sum,以后求解会用到)
所以该问题求解不适用与贪心算法,其实贪心算法适用的问题不是很广泛,比较典型的有活动安排问题,线段覆盖问题,数字组合问题等。
3.采用动态规划求解
动态规划算法的求解步骤分为:划分阶段——>确定状态和状态变量——>找出状态转移方程——>找出递归结束条件。
对于石子合并问题的问题阶段可分为:
当合并的石子为一堆时候:分数为0
当合并的石子为两堆时候:合并分数为相邻两堆石子的个数之和
当合并的石子为三堆时候:合并分数为min(第i堆石子与第i+1石子合并的分数+三堆石子总数,第i+1堆石子与第i+2石子合并的分数+三堆石子总数);
...
状态变量:m[i][j]第i堆至第j堆石子合并时候的分数,stone[i]表示第i堆石子个数
状态转移方程:m[i][j]=min(m[i][k]+m[k+1][j]+sum);sum表示第i堆石子至第j堆石子总数,也是最后一次合并的分数
4.代码实现
#include
#include
#defineN1000
usingnamespacestd;
//求最小合并堆数目
//p[]代表石头数目数组,n表示石堆数目
intminNumber(intp[N],intn)
{
inti=1,j=1,k=1;//用来计数
//定义二维数组m[i][j]来记录i到j的合并过程中的最少石子数目
int**m;
m=newint*[n];
for(i=1;i<=n;i++)
{
m[i]=newint[n];
}
for(i=1;i<=n;i++)
{
for(;j<=n;j++)
m[i][j]=-1;
}
intmin=0;//记录合并最小值
//单独合并
for(i=1;i<=n;i++)
m[i][i]=0;
//相邻两堆石子合并
for(i=1;i<=n-1;i++)
{
j=i+1;
m[i][j]=p[i]+p[j];
}
//相邻三堆至最后的n堆
for(k=3;k<=n;k++)
{
intsum=0;//记录k堆石子总数
for(i=1;i<=n-k+1;i++)
{
j=i+k-1;//记录相邻r堆石子(第i个石子至第j个石子)合并
//先求得这k堆石子的总数
for(intx=i;x<=j;x++)
sum+=p[x];
//第一种情况
m[i][j]=m[i+1][j]+sum;
//计算i到k和k+1到j堆合并时候最小合并值,选择最小值
for(intr=i+1;r<=j-1;r++)
{
intt=m[i][r]+m[r+1][j]+sum;
if(t
m[i][j]=t;
}
}
}
returnm[1][n];
}
//求最大合并值
intmaxNumber(intp[],intn)
{
//记录生成最大合并值的过程中,从i到j合并的最大合并至
inti=1,j=1,k=1;//用来计数
int**m;
m=newint*[n];
for(i=1;i<=n;i++)
{
m[i]=newint[n];
}
for(i=1;i<=n;i++)
for(;j<=n;j++)
m[i][j]=-1;
intmax=0;
//单独组合
for(i=1;i<=n;i++)
m[i][i]=0;
//两两组合
for(i=1;i<=n-1;i++)
{
j=i+1;
m[i][j]=p[i]+p[j];
}
//相邻三堆至第n堆组合时
for(k=3;k<=n;k++)
{
for(i=1;i<=n-k+1;i++)
{
intj=i+k-1;
intsum=0;
for(intr=i;r<=j;r++)
sum+=p[r];
m[i][j]=m[i+1][j]+sum;
for(k=i+1;k
{
intt=m[i][k]+m[k+1][j]+sum;
if(t>m[i][j])
m[i][j]=t;
}
}
}
returnm[1][n];
}
intmain()
{
intstone[N];
intmin=N;
intmax=0;
intn;
inti=1,j=1;
ifstreaminput("input.txt");
ofstreamoutput("output.txt");
input>>n;
while(!input.eof())
{
input>>stone[i];
i++;
}
min=minNumber(stone,n);
max=maxNumber(stone,n);
for(;i<=n-1;i++)
{
intmin_temp=0;
intmax_temp=0;
inttemp=stone[1];
for(intk=2;k<=n;k++)
{
stone[k-1]=stone[k];
}
stone[n]=temp;
min_temp=minNumber(stone,n);
max_temp=maxNumber(stone,n);
if(min_temp
min=min_temp;
if(max_temp>max)
max=max_temp;
}
output<
output<
system("pause");
}