- 目录
钢条切割问题介绍
假定我们知道sering公司出售一段长度为I英寸的钢条的价格为pi(i=1,2,3….)钢条长度为整英寸如图给出价格表的描述(任意长度的钢条价格都有)先给我们一段长度为n的钢条,问怎么切割,获得的收益最大 rn?
钢条价格表:
题目分析:一条n段的钢条,可以分割成1段、2段…n-1段,最多分割成n段来卖,求出售卖价格总数和的最大。
考虑n=4的时候
n段钢条对应的最大收益:
一、递归方法实现
1,不切割 收益为pn(默认此时收益是最大的)
2,将它切割成两半,切割成两半的情况有,对每种情况求最优解,每次将最优解和最大收益进行比较,更新最大收益
(1,n-1) (2,n-2) (3,n-3) (4,n-4) ….. (n-1,1)
对这两半分别求最优解,最优解的和就是当前情况的最优
3,利用递归,将问题细化,对第二步骤的每种情况中的2段钢条继续递归求最优解。
递归方法的问题:效率很低,反复求解相同的子问题。比如对p10分解时,分解为(1,9)(2,8)…,接下来对p9分解时又重复调用了p8。递归方法 递归调用了576次。
代码实现:
using System;
using System.Text;
namespace cchoop
{
class Program
{
public static void Main()
{
//n段钢条对应售卖的价格,数组下标对应钢条的段数
int[] price = { 0, 1, 5, 8, 9, 10, 17, 17, 20, 24, 30 };
Console.WriteLine(MaxPrice(price, 1));
Console.WriteLine(MaxPrice(price, 2));
Console.WriteLine(MaxPrice(price, 3));
Console.WriteLine(MaxPrice(price, 4));
Console.WriteLine(MaxPrice(price, 5));
Console.WriteLine(MaxPrice(price, 6));
Console.WriteLine(MaxPrice(price, 10));
}
/// <summary>
/// 求count段钢条售卖的最大价格(钢条可分割)
/// </summary>
/// <param name="price">n段钢条售卖价格表</param>
/// <param name="count">钢条段数</param>
/// <returns></returns>
public static int MaxPrice(int[] price, int count)
{
int maxPrice = price[count];
for (int i = 1; i < count; i++)
{
int tempMaxPrice = price[i] + MaxPrice(price, count - i);
if (tempMaxPrice > maxPrice)
{
maxPrice = tempMaxPrice;
}
}
return maxPrice;
}
}
}
运行结果:
二、带备忘的自顶向下法(动态规划)
对递归方法进行优化,对于已经求出最优解的n段钢条进行存储,下一次要使用时,直接取值,避免重复求解相同n段钢条的最优解。带备忘的自顶向下法递归调用次数为:17次。
using System;
using System.Text;
namespace cchoop
{
class Program
{
static int num = 0;
static int[] price = { 0, 1, 5, 8, 9, 10, 17, 17, 20, 24, 30 };
static int[] maxProfit = new int[price.Length];
public static void Main()
{
//n段钢条对应售卖的价格,数组下标对应钢条的段数
Console.WriteLine(MaxPrice(price, 0));
Console.WriteLine(MaxPrice(price, 1));
Console.WriteLine(MaxPrice(price, 2));
Console.WriteLine(MaxPrice(price, 3));
Console.WriteLine(MaxPrice(price, 4));
Console.WriteLine(MaxPrice(price, 5));
Console.WriteLine(MaxPrice(price, 6));
Console.WriteLine(MaxPrice(price, 10));
Console.WriteLine("递归次数:" + num);
}
/// <summary>
/// 求count段钢条售卖的最大价格(钢条可分割)
/// </summary>
/// <param name="price">n段钢条售卖价格表</param>
/// <param name="count">钢条段数</param>
/// <returns></returns>
public static int MaxPrice(int[] price, int count)
{
num++;
int maxPrice = price[count];
for (int i = 1; i < count; i++)
{
int maxPriceNext;
if (maxProfit[count - i] != 0)
{
maxPriceNext = maxProfit[count - i];
}
else
{
maxPriceNext = MaxPrice(price, count - i);
maxProfit[count - i] = maxPriceNext;
}
int tempMaxPrice = price[i] + maxPriceNext;
if (tempMaxPrice > maxPrice)
{
maxPrice = tempMaxPrice;
}
}
return maxPrice;
}
}
}
运行结果:
三、自底向上法(动态规划)
自底向上法:首先恰当的定义子问题的规模,使得任何问题的求解都只依赖于更小的子问题的解。因而我们将子问题按照规模排序,按从小到大的顺序求解。当求解某个问题的时候,它所依赖的更小的子问题都已经求解完毕,结果已经保存。
通俗一点讲,就是大规模的最优解依赖小规模的最优解,比如说,求2段钢条的最优解必须知道1段的最优解(此时是1段的本身)进行比较,求3段钢条的最优解必须知道2段的最优解进行比较,以此类推,要求n段的最优解,就必须自底向上n以前的所有段数的最优解。
自底向上法(动态规划)的时间复杂度相比较于递归方法和带备忘的自顶向下法(动态规划),是最低的。
代码实现:
using System;
using System.Text;
namespace cchoop
{
class Program
{
public static void Main()
{
//n段钢条对应售卖的价格,数组下标对应钢条的段数
int[] price = { 0, 1, 5, 8, 9, 10, 17, 17, 20, 24, 30 };
Console.WriteLine(MaxPrice(price, 0));
Console.WriteLine(MaxPrice(price, 1));
Console.WriteLine(MaxPrice(price, 2));
Console.WriteLine(MaxPrice(price, 3));
Console.WriteLine(MaxPrice(price, 4));
Console.WriteLine(MaxPrice(price, 5));
Console.WriteLine(MaxPrice(price, 6));
Console.WriteLine(MaxPrice(price, 10));
}
/// <summary>
/// 求count段钢条售卖的最大价格(钢条可分割)
/// </summary>
/// <param name="price">n段钢条售卖价格表</param>
/// <param name="count">钢条段数</param>
/// <returns></returns>
public static int MaxPrice(int[] price, int count)
{
int length = price.Length;
int[] maxProfit = new int[count + 1];
for (int i = 0; i <= count; i++)
{
int maxPrice = maxProfit[i];
for (int j = 0; j < i; j++)
{
int tempMaxPrice = price[j] + price[i - j];
if (tempMaxPrice > maxPrice)
{
maxPrice = tempMaxPrice;
}
}
maxProfit[i] = maxPrice;
}
return maxProfit[count];
}
}
}
运行结果: