转载请注明原创: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. Wi-Fi定位,AP定位

    Wi-Fi实时定位系统 基于Wi-Fi的无线局域网实时定位系统(Wi-Fi RTLS)结合无线局域网络(WLAN).射频识别(RFID)和实时定位等多种技术,广泛地应用在有无线局域网覆盖的区域,实现复 ...

  2. 【转】Android 使用Scroller实现绚丽的ListView左右滑动删除Item效果

    原文网址:http://blog.csdn.net/xiaanming/article/details/17539199 转帖请注明本文出自xiaanming的博客(http://blog.csdn. ...

  3. Selenium各种工具比较

    Selenium 1.0 Selenium 1.0是第一个基于浏览器的开源自动化测试工具.它可以使用所有支持http库的编程语言,也可以运行在所有支持javascript的浏览器上.当然它 也有它的缺 ...

  4. java中的接口和抽象类是什么?

    抽象类与接口的区别 abstract class和interface是Java语言中对于抽象类定义进行支持的两种机制,正是由于这两种机制的存在,才赋予了Java强大的面向对象能力.abstract c ...

  5. Java学习路线图·影响一代又一代程序员的经典书籍!(转)

    转自:http://www.douban.com/group/topic/50353428/ 基础篇 ·Java核心技术 卷1 基础知识(原书第9版)最新版·中文版 第13届Jolt生产效率大奖获奖图 ...

  6. View Controller 生命周期的各个方法的用法

    (void)awakeFromNib; 这个方法用的时候,outlet还没有连接起来,是view Controller刚从storyboard建的时候,没有完全建好,不过可能有一些事情要在这个方法里面 ...

  7. server的散热和Linux中温度的检測

    当server被放在散热条件不好的条件下,这样会导致硬盘驱动过早损坏,而且server其它的组件也会非常快出现问题. 现代的server主板检測到CPU过热的时候,一般会限制CPU的频率,所以即使se ...

  8. String,StringBuffer与StringBuilder的差别??

    String 字符串常量StringBuffer 字符串变量(线程安全)StringBuilder 字符串变量(非线程安全) 简要的说, String 类型和 StringBuffer 类型的主要性能 ...

  9. 【.NET特供-第三季】ASP.NET MVC系列:MVC与三层图形对照(颠覆性理论)

           在[.NET特供-第三季]系列博客中的第一篇<ASP.NET MVC系列:MVC与三层图形对照>发表之后,引起了领导的注意.同一时候,开发小组内部在交流MVC和三层之间关系的 ...

  10. msxml 操作xml

    1.简介 在.NET平台,微软为C#或托管C++程序员提供了丰富的类库,用以支持各种需求,其中就有对XML文件操作的丰富的类.例如XMLDocument, XmlElement等.但是C++标准库中并 ...