算法导论之钢条切割
对动态规划问题一直理解不够透彻,先从简单的例子开始学习动态规划原理。
问题:
Sering 公司购买长钢条,将其切割为短钢条出售。切割工序本身没有成本支出。公司的管理层希望知道最佳的切割方案。
假定我们知道Serling公司出售一段长度为i英寸的钢条的价格为 (i=1,2,...,单位为美元)。钢条的长度均为英寸钢条和长度价格表如表1所示。
表1
钢条和长度 价格表:
长度 | 价格 |
---|---|
1 | $1 |
2 | $5 |
3 | $8 |
4 | $9 |
5 | $10 |
6 | $17 |
7 | $17 |
8 | $20 |
9 | $24 |
10 | $30 |
钢条切割的问题是这样的:给定一段长度为n英寸的钢条和一个价格表P,求钢条的切割方案,使得销售收益最大。
求解思路:
计算机程序一次只能比较2个数的大小,有大于,等于,小于三种情况。对于钢条来说,假定我们一次切割一次将钢条切割成2段,如果切割成大于2段(没有想清楚)。
可以分为以下三种情况:
1)假定一段是待售的,另一段是待切割的。
2)假定两段都是待售的。
3)假定两段都是待切割的。
情况3可以明显的看出这种思路不能求解出最优解,这种思路作废。对于情况2,这种方式不能求出解,对于一个算法来说需要能够求出子问题,而不是递归的划分成子问题。
思路1)从初始值开始
符号定义:
定义
Vi
为i英寸所能销售的最大价格
假设一个0英寸的钢条,可以切为0英寸,价值为0美元。
假定一个1英寸的钢条,可以切割为1英寸和0英寸,价值为1美元。
假定一个2英寸的钢条,可以切割为(2英寸和0英寸)、(1英寸和1英寸),在这两种方案中选择
V2=5
美元。
假定一个3英寸的钢条,可以切割为(3英寸和0英寸),(2英寸和1英寸),(3个1英寸的方案),在这种方案中选择,可以得出
V3=8
美元。
假定一个n英寸的钢条。可以切割为(n英寸和0英寸),(n-1英寸和1英寸)…等方案。
假定第一段用于出售,第二段用于继续切割,那么这个问题可以归纳为
Vn=max{Pi+Vn−i},i=1,2,...n
式子(1)
程序设计思路
数据结构定义
P数组用于保存价格
V数组用于保存各种长度的钢条所能售出的最大价格。
C数组用于保存各种长度的钢条切割第一段的待售的长度。
从式子1可以看出长度长的钢条求最大销价格需要较短的钢条的求解结果。
从钢条长度为1计算到要求的钢条长度的值,再将此问题分解为小问题遍历所有的可能性进行求解,假定要求的钢条长度为n,程序设计思路如下(伪代码):
//初始化
P = 价格表
V = 0
C = 0
//求解最优值
for i=1:n
{
for(j=1:i)
{
if(V[i] < V[j] + P[i-j])
{
V[i] = V[j] + P[i-j]
C[i] = i-j;
}
}
}
//输出最优值
//最大销售价格
V[n]
//最优解输出
cLength = n
while cLength>0
C[cLength]
cLength -= C[cLength]
java 程序代码如下:
package stellCut;
public class Cut {
public static void main(String[] args)
{
//cutStell(10);
//cutStell(8);
cutStell(25);
}
//要切割钢条的长度
public static void cutStell(int length)
{
int[] V = new int[length+1];
int[] C = new int[length+1];
for(int i=1;i<=length;i++)
{
for(int j=0;j<i;j++)
{
if(V[i] < getPrice(i-j) + V[j])
{
V[i] = getPrice(i-j) + V[j];
C[i] = i-j;
}
}
}
//ouput the value of cut steel
System.out.println("max value is:" + V[length]+"$");
int cl = length;
while(cl>0)
{
System.out.print(" "+ C[cl] +" ");
cl = cl-C[cl];
}
}
//长度价格查询
public static int [] P = {0,1,5,8,9,10,17,17,20,24,30};
public static int getPrice(int length)
{
if(length>10)
return -1;
else
return P[length];
}
}