动态规划-石子合并

动态规划比递归要复杂很多,为了让自己不忘记,还是决定在这个程序中将思考过程写一下,因为发现动态规划类的思考过程其实大同小异。

问题分析

操场玩法情况下  假设有6堆石子,数量分为3,4,6,5,4,2

最初想到用贪心算法思想去看

2+3=5

5+4=9

5+4=9

9+6=15

15+9=24

总计算量=5+9+9+15+24=62

看上去挺完美的,哈哈

接下来就是神奇的地方:

3+4=7

7+6=13

4+2=6

5+6=11

13+11=24

总计算量=7+13+6+11+24=61

贪心算法的本质是得到局部最优,而不能保证全局最优 这里理解局部最优和全部最优很关键


1.分析最优解的结构特征

假设我们已经知道前K堆石子合并,后n-k堆石子合并能得到最优解,那么原问题就变成了两个子问题求最优解。

假设已经知道n堆石子合并的最优计算总量为c,子问题{ai……ak}的计算总量为a,子问题{ak……an}的计算总量为b,i到n的石子数量和为w(i,n),那么c=a+b+w(i,n). 如果c是最优的,那么a和b一定是最优的。也就是所原问题的最优解包含的子问题最优解。

2.建立最优值递归式

设Min[i,j]表示i-j堆石子合并的最小计算量,Min[i,k]表示i-k堆石子合并的最小计算量,Min[k+1,j]表示k-j堆石子合并的最小计算量

                     0  ,i=j

Min[i,j]={ Math.Min(Min[i,k]+Min[k+1,j]+w(i,j))   ,i<j


同理:

设Max[i,j]表示i-j堆石子合并的最小计算量,Max[i,k]表示i-k堆石子合并的最小计算量,Max[k+1,j]表示k-j堆石子合并的最小计算量

                     0  ,i=j

Max[i,j]={ Math.Max(Max[i,k]+Max[k+1,j]+w(i,j))   ,i<j



using System;
using System.Collections.Generic;

namespace 石子合并
{
    class Program
    {

        public static int Count = 6;                           //表示小石子的堆数
        public static readonly int INF = 999999;                        //不可到达值
        public static List<int> M = new List<int>();                //用于储存每堆小石子的数量
        public static List<int> S = new List<int>();                 //用于储存前n堆小石子的数量

        //这边如果只是路边玩法  数组大小只需要Count就行 不需要乘2
        public static int[,] Min = new int[2 * Count, 2 * Count];      //用于存放第i到j堆小石子合并的最小计算量
        public static int[,] Max = new int[2 * Count, 2 * Count];      //用于存放第i到j堆小石子合并的最大计算量

        public static int Min_Cir = 0, Max_Cir = 0;

        static void Main(string[] args)
        {
            //随机生成6个1-30的整数表示每堆小石子的数量
            //并将结果放入M和S两个数组
            int s = 0;
            S.Add(s);
            Random random = new Random();
            for (int i = 0; i < Count; i++)
            {
                int x = random.Next(1, 30);
                s += x;
                M.Add(x);
                S.Add(s);
            }

            Console.Write("小石子的堆数分别是:");
            foreach (var v in M)
                Console.Write(v + "   ");
            Console.WriteLine();

            RoadPlay();

            Console.WriteLine("路边玩法最小花费为:" + Min[0, Count - 1]);
            Console.WriteLine("路边玩法最大花费为:" + Max[0, Count - 1]);

            CircularPlay();
            Console.WriteLine("环形玩法最小花费为:" + Min_Cir);
            Console.WriteLine("环形玩法最大花费为:" + Max_Cir);

            Console.ReadKey();
        }

        static void RoadPlay()
        {
            //初始化结果矩阵
            for (int i = 0; i < Count; i++)
            {
                Min[i, i] = 0;
                Max[i, i] = 0;
            }

            for (int z = 2; z <= Count; z++)                    //合并小石子的堆数
            {
                for (int i = 0; i <= Count - z; i++)             //i表示合并小石子堆的起点
                {
                    int j = z + i - 1;                                      //j表示合并小石子堆的终点
                    Min[i, j] = INF;
                    Max[i, j] = -1;
                    int temp = S[j + 1] - S[i];                         //记录i至j之间小石子数之和
                    for (int k = i; k < j; k++)                        //i至j中间点
                    {
                        Min[i, j] = Math.Min(Min[i, j], Min[i, k] + Min[k + 1, j] + temp);
                        Max[i, j] = Math.Max(Max[i, j], Max[i, k] + Max[k + 1, j] + temp);
                    }
                }
            }

        }

        /// <summary>
        /// 环形玩法可以转换为直线玩法
        ///将环看成长度为2*n-1的直线来处理
        /// </summary>
        static void CircularPlay()
        {

            for (int i = 0; i < Count - 1; i++)
            {
                M.Add(M[i]);
                S.Add(S[Count + i] + M[i]);
            }

            Count = 2 * Count - 1;
            RoadPlay();
            Count = (Count + 1) / 2;

            //找最优解
            Min_Cir = Min[0, Count - 1];
            Max_Cir = Max[0, Count - 1];
            for (int i = 1; i < Count; i++)
            {
                if (Min[i, i + Count - 1] < Min_Cir)
                    Min_Cir = Min[i, i + Count - 1];
                if (Max[i, i + Count - 1] > Max_Cir)
                    Max_Cir = Max[i, i + Count - 1];
            }
        }
    }
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值