动态规划

     感觉自己对抽象的东西实在是很难理解, 就写一篇日志记录一下自己的学习过程。
     什么是动态规划?        
        动态规划是解决多阶段决策过程最优化问题的一种方 把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解(前一个阶段为后一个阶段留下了一些有用的信息),在求任一子问题时,通过决策保留有可能达到最优的局部解,丢弃其他局部解。
 解决各个阶段的子问题后,最后一个子问题的最优解就是最终的最优解。 
重要的几个性质?
   最优化原理:最优解所包含的子问题的解也是最优的。也就是说该问题具有最优子结构(不同于分治法, 分治的子问题是相互独立的)
   无后效性:每一个子问题的决策不会影响到后面未解决的问题的决策。
    重叠子问题: 也就是说子问题之间不是独立的,子问题在下一次决策会被多次用到(一个问题的子问题所得的结果被重复计算,做法:自顶向下记忆化处理, 自底向上迭代处理)。
    分治法分解后的子问题是相互独立的, 而动态规划分解后的子问题是相互联系的。

贪心的子问题最优解与动态规划子问题最优解的区别?
贪心:每一个阶段都是最优解(贪心选择)。 局部最优解->全局最优解。动态规划:当前最优解只和上一步的最优解有关。局部最优解->全局最优解(不一定成立)

解决问题步骤:
1.确定阶段:按照问题的时间或空间等特征将其划分为若干阶段, 划分后的每一个阶段必须是有序的。
2.确定状态:将问题发展的各个阶段的状态表示出来,需要满足无后效性。
3.确定决策和写出状态转移方程:根据上一状态决策本阶段的状态。(相邻两个状态间存在某种联系)
4.寻找边界条件:状态转移方程是一个递推式, 需要找到递推式的终止条件。

何时使用动态规划?
满足最优化原理和无后效性即可使用动态规划,动态规划的优势在于处理重叠子问题的问题,其精髓在于将求解过程划分成N个子阶段,并且将每个子阶段的结果存储在一张表中以便后续求解时直接调用前面已有的结果,从而避免了大量的重复计算,大大提高了计算的效率。

举一个最大乘积的例子:  
问题描述
  今年是国际数学联盟确定的“2000——世界数学年”,又恰逢我国著名数学家华罗庚先生诞辰90周年。在华罗庚先生的家乡江苏金坛,组织了一场别开生面的数学智力竞赛的活动,你的一个好朋友XZ也有幸得以参加。活动中,主持人给所有参加活动的选手出了这样一道题目:

  设有一个长度为N的数字串,要求选手使用K个乘号将它分成K+1个部分,找出一种分法,使得这K+1个部分的乘积能够为最大。

  同时,为了帮助选手能够正确理解题意,主持人还举了如下的一个例子:

  有一个数字串:312, 当N=3,K=1时会有以下两种分法:

  3*12=36
  31*2=62

  这时,符合题目要求的结果是:31*2=62

  现在,请你帮助你的好朋友XZ设计一个程序,求得正确的答案。
   输入格式
  程序的输入共有两行:
  第一行共有2个自然数N,K(6≤N≤40,1≤K≤6)
  第二行是一个长度为N的数字串。
解题思路:
阶段:k个阶段, 每个阶段插入一个乘号
状态: dp[i][j],表示前i位数插入j个乘号所得的最优解
状态转移方程:dp[i][j] = max(dp[i][j], dp[k][j - 1] * num[k + 1][i]) (子问题,前k位插入j - 1个乘号所得的最优解 *第 k + 1位到第i位所得的值)
寻找边界: 初始化:dp[i][0], 前i位插入0个乘号, 因为要求最大值, 且所得结果大于0, 所以其他状态初始化为0
简单来说, 就是枚举所有情况, 标记已经计算过的值
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm> 
using namespace std;
typedef unsigned long long bignum;
bignum f[41][31];
bignum num[41][41];
char str[42];

 
int main()
{
    int i, j, l;
    int n, k;
    scanf("%d%d", &n, &k); //输入数字位数及插入乘号个数 
    scanf("%s", &str[1]);//输入n位数 
    for(i = 1; i <= n; i++){
        for(j = i; j <= n; j++){
            num[i][j] = num[i][j - 1] * 10 + str[j] - '0'; //计算数字串从第i位到第j位的十进制数的大小 
            cout << num[i][j] << " ";
        }
        cout << endl;
    }
    for(i = 1; i <= n; i++){
        f[i][0] = num[1][i];
cout << f[i][0] << " " << num[1][i] <<  endl; 
    }
    for(j = 1; j <= k; j++){
        for(i = 2; i <= n; i++){
            for(l = 1; l < i; l++){
                f[i][j] = max(f[i][j], f[l][j - 1] * num[l + 1][i]);//f[i][j]表示前i个数插入j个乘号所得的值 
                cout << f[i][j] << " " << f[i][j] << " " << f[l][j - 1] << "*" << num[l + 1][i] << endl;
            }
        }
    }
    printf("%lld\n", f[n][k]);
    return 0;
}




动态规划的概念字面上似乎很好理解, 但是不一定真正完全领会, 遇到的实际问题往往都是很抽象的, 需要搞清楚每一个步骤, 特别是阶段的划分和状态的确定。只有搞清楚这两个, 才能做出相应的决策, 以及写出状态转移方程。写到这里, 感觉自己懂了那么一丢丢, 还是有效果的。 有问题欢迎指出!


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值