动态规划 - 钢条切割问题

  • 目录


钢条切割问题介绍

假定我们知道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];
        }
    }
}

运行结果:
这里写图片描述


猜你喜欢

转载自blog.csdn.net/qq_34937637/article/details/81508332