• 动态规划:伐木问题


    伐木问题

    一片环形的林带按照时钟方向分成N块相邻的林地,首尾相连,每一块林地均与另两块林地相邻。伐木工对每一块林地里可采伐的木材量进行了估计,这些木材量以 int[] woods 来表示。这个数组按顺时针顺序列出了每一块林地里的可采伐木材量。

    现在要在这片林带中选择一些林地来进行采伐。但为了不破坏生态,不能有两块相邻的林地同时被采伐。你必须编写程序来计算最大可采伐的木材量。

    函数声明:int maxWoods(int[] woods)

    参数范围:woods可以包含2到50个元素。每一个元素的取值范围为1到1000。

    测试用例:

    1:{10, 3, 2, 5, 7, 8}

    输出:19

    注:因为林地首尾相连,所以不能同时取10和8。在此限制下,最大的开采量为10+2+7=19。

    2:{11, 15}

    输出:15

    3:{7, 7, 7, 7, 7, 7, 7}

    输出:21

    4:{1, 2, 3, 4, 5, 1, 2, 3, 4, 5}

    输出:16

    5:{94, 40, 49, 65, 21, 21, 106, 80, 92, 81, 679, 4, 61, 6, 237, 12, 72, 74, 29, 95, 265, 35, 47, 1, 61, 397, 52, 72, 37, 51, 1, 81, 45, 435, 7, 36, 57, 86, 81, 72}

    输出:2926

    分析:

    状态转移方程:

    两种状态:

        1).包含i 

        2).不包含i

    情况一:包含i

    F(i) = Max{F(i-2) + a[i], a[i]},所以当F(i-2) < 0,时, F(i) = a[i],else: F(i) = F(i-2)+a[i]

    情况二:不包含i

    F(i) = F(i-1)

    综合一二: F(i)= Max{Max{F(i-2)+a[i],a[i]}, F(i-1)}

    初始化:

    F(0)= a[0]

    F(1) = Max{a[0], a[1]}

    具体实现有下面三个方法:

    方法一:

    1. 递归实现

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace ConsoleApplication4
    {
        class Program
        {
            static void Main(string[] args)
            {
                int[] input0 = { 11, 15 };
                // GetMaxSubArrayValue(input0);
                int[] input1 = { 94, 40, 49, 65, 21, 21, 106, 80, 92, 81, 679, 4, 61, 6, 237, 12, 72, 74, 29, 95, 265, 35, 47, 1, 61, 397, 52, 72, 37, 51, 1, 81, 45, 435, 7, 36, 57, 86, 81, 72 };
                int temp = GetMaxGroupValueOuter(input1);
    
            }
    
            // 方法一: 采用递归
            public static int GetMaxGroupValueOuter(int[] arrary)
            {
                int endIndex = arrary.Length - 1;
                int startIndex = 0;
    
                if (endIndex == 0)
                {
                    return arrary[0];
                }
    
                if (endIndex == 1)
                {
                    return Math.Max(arrary[0], arrary[1]);
                }
    
                int temp1 = GetMaxGroupValue(arrary, startIndex, endIndex - 1);
                int temp2 = GetMaxGroupValue(arrary, startIndex + 1, endIndex);
                return Math.Max(temp1, temp2);
            }
    
            // 采用递归
            public static int GetMaxGroupValue(int[] arrary, int startIndex, int endIndex)
            {
                if (startIndex == 0
                 || startIndex == 1)
                {
                    if (endIndex == startIndex)
                    {
                        return arrary[startIndex];
                    }
    
                    if (endIndex == startIndex + 1)
                    {
                        return Math.Max(arrary[startIndex], arrary[startIndex + 1]);
                    }
                }
                
                int temp1 = GetMaxGroupValue(arrary, startIndex, endIndex - 2);
    
                if (temp1 < 0) //F(i-2)  <0;
                {
                    temp1 = arrary[endIndex];
                    // 
                    //f(i)  = Max(F(i), F(i-2) +a[i], F(i-1)};
                    //
                }
                else
                {
                    temp1 += arrary[endIndex];
                }
    
                int temp2 = GetMaxGroupValue(arrary, startIndex, endIndex - 1);
    
                return Math.Max(temp1, temp2);
            }
        }
    }

    方法二:

       1.利用空间来换取时间,提高效率

       2.自底而上,顺序求解

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace ConsoleApplication4
    {
        class Program
        {
            static void Main(string[] args)
            {
                int[] input0 = { 11, 15 };
                // GetMaxSubArrayValue(input0);
                int[] input1 = { 94, 40, 49, 65, 21, 21, 106, 80, 92, 81, 679, 4, 61, 6, 237, 12, 72, 74, 29, 95, 265, 35, 47, 1, 61, 397, 52, 72, 37, 51, 1, 81, 45, 435, 7, 36, 57, 86, 81, 72 };
                int temp = GetMaxGroupValueOuter2(input1);
    
            }
            public static int GetMaxGroupValueOuter2(int[] arrary)
            {
                int endIndex = arrary.Length - 1;
                int startIndex = 0;
    
                if (endIndex == 0)
                {
                    return arrary[0];
                }
    
                if (endIndex == 1)
                {
                    return Math.Max(arrary[0], arrary[1]);
                }
    
                int temp1 = GetGroupMax(arrary, startIndex, endIndex - 1);
                int temp2 = GetGroupMax(arrary, startIndex + 1, endIndex);
                return Math.Max(temp1, temp2);
            }
    
            // 方法二。自底而上,顺序求解
            public static int GetGroupMax(int[] array, int startIndex, int endIndex)
            {
                if (startIndex == 0
                    || startIndex == 1)
                {
                    if (endIndex == startIndex)
                    {
                        return array[startIndex];
                    }
    
                    if (endIndex == startIndex + 1)
                    {
                        return Math.Max(array[startIndex], array[startIndex + 1]);
                    }
                }
    
                int[] values = new int[endIndex - startIndex + 1];
    
                for (int j = 0; j < endIndex - startIndex + 1; j++)
                {
                    values[j] = int.MinValue;
                }
    
                //初始化F(0), F(1)
                values[0] = array[startIndex];
                values[1] = Math.Max(array[startIndex], array[startIndex + 1]);
    
                // 初始化sum
                int index = 0;           
    
                for (int i = 2; i < endIndex - startIndex + 1; i++)
                {
                    if (values[i - 2] > 0)
                    {
                        values[i] = Math.Max(values[i - 1], values[i - 2] + array[i + startIndex]);
                    }
                    else
                    {
                        values[i] = Math.Max(values[i - 1], array[i]);
                    }
                }
    
                for (int i = 1; i < endIndex - startIndex + 1; i++)
                {
                    if (values[i] > values[index])
                    {
                        index = i;
                    }
                }
    
                return values[index];
            }
        }
    
    }
    
    

    方法三

     1.采用迭代

    2. 自底而上,顺序求解,优化空间

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace ConsoleApplication4
    {
        class Program
        {
            static void Main(string[] args)
            {
                int[] input0 = { 11, 15 };
                // GetMaxSubArrayValue(input0);
                int[] input1 = { 94, 40, 49, 65, 21, 21, 106, 80, 92, 81, 679, 4, 61, 6, 237, 12, 72, 74, 29, 95, 265, 35, 47, 1, 61, 397, 52, 72, 37, 51, 1, 81, 45, 435, 7, 36, 57, 86, 81, 72 };
    
                int temp = GetMaxGroupValueOuter3(input1);
            }
            // 方法三:采用迭代,自底而上,顺序求解,优化空间
            public static int GetMaxGroupValueOuter3(int[] arrary)
            {
                int endIndex = arrary.Length - 1;
                int startIndex = 0;
    
                if (endIndex == 0)
                {
                    return arrary[0];
                }
    
                if (endIndex == 1)
                {
                    return Math.Max(arrary[0], arrary[1]);
                }
    
                int temp1 = GetMaxSubArrayValue(arrary, startIndex, endIndex - 1);
                int temp2 = GetMaxSubArrayValue(arrary, startIndex + 1, endIndex);
                return Math.Max(temp1, temp2);
            }
    
            // 求不连续数组的子数组之和的最大值.
            public static int GetMaxSubArrayValue(int[] array, int startIndex, int endIndex)
            {
                int sum = array[0];
    
                int temp1 = array[startIndex]; // F(i-2)
                int temp2 = array[startIndex + 1];  // F(i-1)
    
                // F(i)  = Max (F(i-2) + array[i], array[i], F(i-1)
                // 采用迭代更替的方法,逐步更新. temp2代表 F(i-1)的最大值,temp1比temp2始终慢一步,代表F(i-2)的值.
    
                if (startIndex == 0
                   || startIndex == 1)
                {
                    if (endIndex == startIndex)
                    {
                        return array[startIndex];
                    }
    
                    if (endIndex == startIndex + 1)
                    {
                        return Math.Max(array[startIndex], array[startIndex + 1]);
                    }
                }
    
                for (int i = startIndex + 2; i <= endIndex; i++)
                {
                    if (temp1 < 0)
                    {    //情况一。 F(i-2) < 0;很明显 F(i-2) + a[i] < a[i]
                        temp1 = array[i];
                        //s = e = i;
                    }
                    else //情况二 F(i-2) = F(i-2)+a[i]
                    {
                        temp1 += array[i];
                        // e = i;
                    }
    
                    // 到这一步时, F(i-2) = Max{(F(i-2), F(i-2) + a[i])}
    
                    // 情况三。不包含a[i]的情况,即a[0]~a[i-1]的最大值,设为sum.
    
                    if (temp1 > sum) //用F(i-2)和sum比较
                    {
                        sum = temp1;
                    }
    
                    temp1 = temp2;
                    temp2 = sum;
    
                }
    
                return sum;
            }
        }
           
    }

    总结:1. 此题非常类似于2.14 求数组的子数组之和的最大值和最小值(动态规划),但又有所不同,主要在于此题的不连续性。所以才用在方法二中采用两个临时变量,来表示当前的最大值,和前一个最大值,交替实现。别的地方恶化子数组之和的最大值问题完全一样。

       2. 此题实际上是由不连续子数组最大和问题延伸而来

             http://www.cnblogs.com/freewater/archive/2012/08/18/2645777.html

             3.  和01背包问题也非常类似(选i和不选i)

              http://www.cnblogs.com/jiangjun/archive/2012/05/08/2489590.html

    
    
    
  • 相关阅读:
    二维树状数组(模板)
    3033太鼓达人
    2503相框
    Ant Trip(画几笔)
    [ZJOI2004]嗅探器
    [USACO06JAN]冗余路径Redundant Paths(缩点)
    P3806 【模板】点分治1
    P4149 [IOI2011]Race
    P2634 [国家集训队]聪聪可可
    P4178 Tree
  • 原文地址:https://www.cnblogs.com/Jessy/p/3120644.html
Copyright © 2020-2023  润新知