转载请注明原创:http://www.cnblogs.com/StartoverX/p/4603173.html

   Dynamic Programming的Programming指的不是程序而是一种表格法。我们知道,分治法将问题划分为互不相交的子问题,递归的求解子问题,再将他们组合起来,求出原问题的解。而动态规划应用于子问题重叠的情况,即不同的子问题具有公共的子子问题,在这种情况下,动态规划方法对每个子子问题只求解一次,将其解保存在一个表格中,从而无需每次求解一个子子问题时都重新计算。

  动态规划方法通常用来求解最优化问题(optimization problem),也就是找到问题的一个最优解。我们通常按以下四个步骤来设计一个动态规划算法:

    1.刻画一个最优解的结构特征。

    2.递归地定义最优解的值。

    3.计算最优解的值,通常采用自底向上的方法。  

    4.利用计算出的信息构造一个最优解。

  第一步刻画一个最优解的结构特征。如果一个问题的最优解包含其子问题的最优解,我们就称此问题具有最优子结构性质。当某问题具有最优子结构性质时,如果这些子问题有重叠的情况,我们就应考虑动态规划方法(具有最优子结构性质也可能意味着适合应用贪心策略)。

  第二步中,由于问题具有子问题,所以我们可以使用递归的方式求解,但是由于反复的求解相同的子问题,朴素的递归算法非常的低效。

  第三步,我们开始真正实现动态规划算法,动态规划算法有两种等价的实现方法:

    1.带备忘的自顶向下法(top-down with memoization):在第二步递归方法的过程中,保存每一个子问题的解,当需要一个子问题的解时,过程首先检查是否已经保存过此解。如果是,则直接返回保存的值,从而节省了计算时间。否则,按通常方式计算此解后保存。我们称这个递归过程是带备忘的(memoized),因为它“记住”了之前已经计算出的结果。

    2.自底向上法(bottom-up method):将子问题按规模排序,按由小至大的顺序进行求解,当求解某个子问题时,它所依赖的那些更小的子问题都已求解完毕,结果已经保存。每个子问题只需求解一次,当我们第一次遇见它时,它的所有前提子问题都已求解完成。

  第四步中构造最优解,要在第三步中保存每次求解时维护一些额外的信息保存做出的选择,第四步再将每次的选择得出,就可以构造出一个最优解。

  举例:Leetcode word-break:

    Given a string s and a dictionary of words dict, determine if s can be segmented into a space-separated sequence of one or more dictionary words.

    For example, given

      s = "leetcode",
      dict = ["leet", "code"].     Return true because "leetcode" can be segmented as "leet code".

  分析:输入是一个unordered-set<string>& wordDict,一个string& s,输出是一个bool值,判断string s是否能被分解成wordDict中的string值。

  分析该问题我们发现,如果s能够被分解成wordDict中的单词组合,那么,对于其分割答案中的一次分割s->s1,s2,将s分割成s1和s2,s1和s2必也能分割成wordDict中的单词。也就是对s的分割包含了对s1和s2的分割,该问题的最优解包含了其子问题的最优解,该问题具有最优子结构性质。

  现在我们可以尝试通过递归的方法求解该问题,我们对于s中每两个字符间的每一次分割遍历s,如果分割得到的s1和s2都能被分解为wordDict中的单词,我们就说s能够被分解为wordDict中的单词,如果s1和s2不能被分解为wordDict中的单词,则继续遍历s。如果分割点直到s中的最后一个字符都没能得到两个都能被分解的s1和s2,则我们说s不能分解为s1和s2中的单词。

#include <iostream>
#include <vector>
#include <string>
#include <unordered_set>
#include <algorithm>
using namespace std; class Solution
{
public:
bool wordBreak(string s,unordered_set<string>& wordDict)
{
if(wordDict.size() == ) //边界条件1
{
return false;
}
if(s.size() == ) //边界条件2
{
return false;
}
if(find(wordDict.begin(),wordDict.end(),s) != wordDict.end()) //递归退出条件
{
return true;
}
     int size = s.size();
if(size == ) //如果wordDict中没找到s,s又不能继续分割,return false
{
return false;
}
for(int i=;i<size;i++) //对s遍历每一个分割点
{
string s1 = s.substr(,i);
string s2 = s.substr(i,size);
if(wordBreak(s1,wordDict) && wordBreak(s2,wordDict))
{
return true;
}
}
return false;
}
};

  分析上述递归的方法,我们发现,对于每一个s,我们都要遍历它的每两个字符间的分割点,而s分割出来的s1,s2,又包含了同样的字符顺序,也就是,我们重复求解了很多次相通的字符串,所以,上面的递归解法非常的低效。由于我们判断出该题的子问题相互重叠,我们使用动态规划的方法。

  上面已经得到了一个递归解法,所以在动态规划解法中我们使用带备忘的自顶向下法对于此问题,我们要求的解是对于一个特定的string,求对应的bool值,所以我们使用一个全局map:map<string,bool> mp来保存每次的结果。在每次递归前检查是否已经取得了解,并在递归后在map中保存解。

#include <iostream>
#include <vector>
#include <string>
#include <unordered_set>
#include <algorithm>
#include <map>
using namespace std; class Solution
{
public:
map<string,bool> mp;
bool wordBreak(string s,unordered_set<string>& wordDict)
{
if(wordDict.size() == ) //边界条件1
{
return false;
}
if(s.size() == ) //边界条件2
{
return false;
}
if(find(wordDict.begin(),wordDict.end(),s) != wordDict.end()) //递归退出条件
{
return true;
}
int size = s.size();
if(size == ) //如果wordDict中没找到s,s又不能继续分割,return false
{
return false;
}
for(int i=;i<size;i++) //对s遍历每一个分割点
{
string s1 = s.substr(,i);
string s2 = s.substr(i,size);
bool flag1 = false;
bool flag2 = false;
bool is1 = false;
bool is2 = false;
if(mp.empty() == false && (mp.find(s1) != mp.end()))//如果已经得到了s1的解,直接从mp中取。
{
is1 = true;
flag1 = (*mp.find(s1)).second;
}
if(mp.empty() == false && (mp.find(s2) != mp.end()))//如果已经得到了s2的解,直接从mp中取。
{
is2 = true;
flag2 = (*mp.find(s2)).second;
}
if(is1 == false)//如果没有得到过s1的解,求解并保存在mp中。
{
flag1 = wordBreak(s1,wordDict);
mp[s1] = flag1;
}
if(is2 == false)//如果没有得到过s2的解,求解并保存在mp中。
{
flag2 = wordBreak(s2,wordDict);
mp[s2] = flag2;
}
if(flag1 && flag2)
{
return true;
}
}
return false;
}
};

[算法]动态规划(Dynamic programming)的更多相关文章

  1. 动态规划算法(Dynamic Programming,简称 DP)

    动态规划算法(Dynamic Programming,简称 DP) 浅谈动态规划 动态规划算法(Dynamic Programming,简称 DP)似乎是一种很高深莫测的算法,你会在一些面试或算法书籍 ...

  2. 动态规划(Dynamic Programming)算法与LC实例的理解

    动态规划(Dynamic Programming)算法与LC实例的理解 希望通过写下来自己学习历程的方式帮助自己加深对知识的理解,也帮助其他人更好地学习,少走弯路.也欢迎大家来给我的Github的Le ...

  3. 算法-动态规划 Dynamic Programming--从菜鸟到老鸟

    算法-动态规划 Dynamic Programming--从菜鸟到老鸟      版权声明:本文为博主原创文章,转载请标明出处. https://blog.csdn.net/u013309870/ar ...

  4. 动态规划Dynamic Programming

    动态规划Dynamic Programming code教你做人:DP其实不算是一种算法,而是一种思想/思路,分阶段决策的思路 理解动态规划: 递归与动态规划的联系与区别 -> 记忆化搜索 -& ...

  5. 6专题总结-动态规划dynamic programming

    专题6--动态规划 1.动态规划基础知识 什么情况下可能是动态规划?满足下面三个条件之一:1. Maximum/Minimum -- 最大最小,最长,最短:写程序一般有max/min.2. Yes/N ...

  6. 以计算斐波那契数列为例说说动态规划算法(Dynamic Programming Algorithm Overlapping subproblems Optimal substructure Memoization Tabulation)

    动态规划(Dynamic Programming)是求解决策过程(decision process)最优化的数学方法.它的名字和动态没有关系,是Richard Bellman为了唬人而取的. 动态规划 ...

  7. Python算法之动态规划(Dynamic Programming)解析:二维矩阵中的醉汉(魔改版leetcode出界的路径数)

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_168 现在很多互联网企业学聪明了,知道应聘者有目的性的刷Leetcode原题,用来应付算法题面试,所以开始对这些题进行" ...

  8. 动态规划 Dynamic Programming

    March 26, 2013 作者:Hawstein 出处:http://hawstein.com/posts/dp-novice-to-advanced.html 声明:本文采用以下协议进行授权: ...

  9. 最优化问题 Optimization Problems & 动态规划 Dynamic Programming

    2018-01-12 22:50:06 一.优化问题 优化问题用数学的角度来分析就是去求一个函数或者说方程的极大值或者极小值,通常这种优化问题是有约束条件的,所以也被称为约束优化问题. 约束优化问题( ...

随机推荐

  1. 【HDOJ】1244 Max Sum Plus Plus Plus

    这题目一直wa,原来是因为我把JUDGE写错了,对拍了一下午都没检查出来.水DP啊. #include <cstdio> #include <cstring> #include ...

  2. COJ 0503 比赛

    比赛 难度级别:D: 运行时间限制:2000ms: 运行空间限制:51200KB: 代码长度限制:2000000B 试题描述 初三年级举办了一场篮球赛,共有N个班级参加.当WZJ知道了这件事情, 已经 ...

  3. C#中两个时间的比较

    var DateTimedt1 = Convert.ToDateTime(dt);     var DateTimedt2 = Convert.ToDateTime(DateTime.Now);    ...

  4. c++ map 插入数据后,begin(),end()以及当前迭代器的变化

    1. map.end()指向map的最后一个元素之后的地址,无论执行map.erase(iter)还是map.add(key, value),map.end()所返回的值永远不会发生变化,都是指向同一 ...

  5. H - Can you answer these queries? - (区间查询更新)

    有一列数,(都是2^63范围内的并且都大于0的整数),现在呢有一些操作, 操作 0 可以把区间LR内的所有数都变成它的平方根数(是取整后的),操作 1 可以就是求区间LR内的和了. 分析:因为这个操作 ...

  6. B - I Hate It - hdu 1754

    Description 很多学校流行一种比较的习惯.老师们很喜欢询问,从某某到某某当中,分数最高的是多少. 这让很多学生很反感. 不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟 ...

  7. python:时间处理模块

    # coding=utf-8 from datetime import date, datetime import time def date_test(): print 'date.max', da ...

  8. Python学习 1 一 Python2.75的安装及环境配置教程

    Python2.75的安装及环境配置教程 Python的语法简洁,功能强大,有大量的第三方开发包(模块),非常适合初学者上手.同时Python不像java一样对内存要求非常高,适合做一些经常性的任务方 ...

  9. Eclipse自动插件依赖的一种配置解决方式

    Eclipse的插件具有以下特点: (1)每一个插件有自己独立的classloader (2)插件资源的交互通过MENIFEST.MF中"Export-Package, Require-Bu ...

  10. 《C专家变成》之二

    第五章 一.对链接的思考 目标文件并不能直接执行,它首先需要载入到链接器.链接器确认main函数为初始进入点(程序开始执行的地方),把符号引用绑定到内存地址,把所有的目标文件集中在一起,再加上库文件, ...