一、问题描述:
设有一根长度为L的钢条,在钢条上标有n个位置点(p1,p2,......,pn)。现在需要按钢条上标注的位置将钢条切割为n+1段,每次切割的代价为所切的钢条长度,试求在所有的切割方案中的总代价的最小值。
二、问题分析:
这是一道利用动态规划算法解决的问题,原因如下:(1)该问题具有最优子结构性质。当我们确定第一次切割的点时,切割后形成的两根小钢管也应按照最小的代价切割,否则我们拿最小代价的切割方案来代替现有切割方案,整根钢管切割的总代价将会减小,这就会产生矛盾。(2)该问题具有重叠子问题。相同的子问题会在我们确定不同切割点的时候反复出现。
三、递推关系:
我们来考察在位置点pi到位置点pj之间进行切割的情况,cost[i][j]为这个问题的总代价(i=1,2,......,n ; j=1,2,......,n),那么:
当i>j时,不需要切割:cost[i][j]=0;
当i=j时,仅有一个切割点:cost[i][j]=d(i+1)-d(i-1);
当i<j时,有多个切割点:cost[i][j]=min{cost[i][k-1]+cost[k+1][j]+d(j+1)-d(i-1)};其中k=i,i+1,......,j。
四、完整代码:
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int L,n;//L为完整钢管总长度,n为切割点的数目
cout<<"请输入钢管总长度和切割点数目:";
cin>>L>>n;
int *p;//记录n个切割点位置坐标的数组
p=new int[n+2];//多出来的2是钢管的左端点(起点)和右端点(终点)
p[0]=0;p[n+1]=L;//将左端点视为原点
int i,j,step;//i,j为简单的循环所需变量,step为i,j之间的步长
for(i=1;i<=n;i++)
{
cout<<"请输入第"<<i<<"个切割点的位置坐标:" ;
cin>>p[i];
}
sort(p,p+n+2);//将坐标从小到大排列
int **cost=new int*[n+2];
for(i=0;i<n+2;i++)
{
cost[i]=new int[n+2];
}//动态创建二维数组,cost[i][j]的意义见递推关系解释
for(i=0;i<n+2;i++)
{
for(j=0;j<n+2;j++)
{
if(i>j)
{
cost[i][j]=0;
}
else if(i==j && i!=0 && i!=n+1)//这些限制条件防止数组超界
{
cost[i][j]=p[i+1]-p[i-1];
}
}
}
cost[0][0]=0;cost[n+1][n+1]=0;//额外初始化
for(step=1;step<=n-1;step++)
{
for(i=1;i<=n;i++)
{
j=i+step;
int CutPoint=i;//定义切点
int MyCostMin=cost[i][CutPoint-1]+cost[CutPoint+1][j]+(p[j+1]-p[i-1]);//定义最小代价
for(CutPoint=i+1;CutPoint<=j;CutPoint++)
{
if(j<n+1)//防止数组越界
{
int PreCost=cost[i][CutPoint-1]+cost[CutPoint+1][j]+(p[j+1]-p[i-1]);//定义当前代价
if(PreCost<MyCostMin)
{
MyCostMin=PreCost;
}
}
}
cost[i][j]=MyCostMin;
}
}
cout<<"需要的最小总代价为:"<<cost[1][n];
}
五、运行结果: