[算法]动态规划(Dynamic programming)
转载请注明原创: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)的更多相关文章
- 动态规划算法(Dynamic Programming,简称 DP)
动态规划算法(Dynamic Programming,简称 DP) 浅谈动态规划 动态规划算法(Dynamic Programming,简称 DP)似乎是一种很高深莫测的算法,你会在一些面试或算法书籍 ...
- 动态规划(Dynamic Programming)算法与LC实例的理解
动态规划(Dynamic Programming)算法与LC实例的理解 希望通过写下来自己学习历程的方式帮助自己加深对知识的理解,也帮助其他人更好地学习,少走弯路.也欢迎大家来给我的Github的Le ...
- 算法-动态规划 Dynamic Programming--从菜鸟到老鸟
算法-动态规划 Dynamic Programming--从菜鸟到老鸟 版权声明:本文为博主原创文章,转载请标明出处. https://blog.csdn.net/u013309870/ar ...
- 动态规划Dynamic Programming
动态规划Dynamic Programming code教你做人:DP其实不算是一种算法,而是一种思想/思路,分阶段决策的思路 理解动态规划: 递归与动态规划的联系与区别 -> 记忆化搜索 -& ...
- 6专题总结-动态规划dynamic programming
专题6--动态规划 1.动态规划基础知识 什么情况下可能是动态规划?满足下面三个条件之一:1. Maximum/Minimum -- 最大最小,最长,最短:写程序一般有max/min.2. Yes/N ...
- 以计算斐波那契数列为例说说动态规划算法(Dynamic Programming Algorithm Overlapping subproblems Optimal substructure Memoization Tabulation)
动态规划(Dynamic Programming)是求解决策过程(decision process)最优化的数学方法.它的名字和动态没有关系,是Richard Bellman为了唬人而取的. 动态规划 ...
- Python算法之动态规划(Dynamic Programming)解析:二维矩阵中的醉汉(魔改版leetcode出界的路径数)
原文转载自「刘悦的技术博客」https://v3u.cn/a_id_168 现在很多互联网企业学聪明了,知道应聘者有目的性的刷Leetcode原题,用来应付算法题面试,所以开始对这些题进行" ...
- 动态规划 Dynamic Programming
March 26, 2013 作者:Hawstein 出处:http://hawstein.com/posts/dp-novice-to-advanced.html 声明:本文采用以下协议进行授权: ...
- 最优化问题 Optimization Problems & 动态规划 Dynamic Programming
2018-01-12 22:50:06 一.优化问题 优化问题用数学的角度来分析就是去求一个函数或者说方程的极大值或者极小值,通常这种优化问题是有约束条件的,所以也被称为约束优化问题. 约束优化问题( ...
随机推荐
- 高质量JavaScript代码书写基本要点学习
高质量JavaScript代码书写基本要点学习 可维护的代码意味着: •可读的 •一致的 •可预测的 •看上去就像是同一个人写的 •已记录 最小全局变量(Minimizing Globals) ...
- CentOS 6.5 安装realtek RTL8188CE无线网卡
首先,要检查一下网络适配器的型号. [root@localhost sam]# lspci -nn | grep -i net03:00.0 Ethernet controller [0200]: R ...
- 辗转相除法_欧几里得算法_java的实现(求最大公约数)
辗转相除法,又被称为欧几里德(Euclidean)算法, 是求最大公约数的算法. 当然也可以求最小公倍数. 算法描述 两个数a,b的最大公约数记为GCD(a,b).a,b的最大公约数是两个数的公共素因 ...
- HDU 3853 LOOPS
题意:对于每一格,都可以往右走,原地不走,往下走,概率分别为a[i],b[i],c[i](每一个格子与其他格子的概率不一定相同).在R*C的棋盘上(输入数据保证不会走出棋盘),求从(0, 0)走到(R ...
- 终于有人把O2O、C2C、B2B、B2C的区别讲透了
一.O2O.C2C.B2B.B2C的区别在哪里? o2o 是 online to offline 分为四种运营模式 1.online to offline 是线上交易到线下消费体验 2.offline ...
- python_安装工具easy_install和pip
前言 用python就必须知道easy_install和pip这两个东西啦 easy_insall提供了在线一键安装模块或包的方便方式,而pip是easy_install的改进版,提供更好的提示信息, ...
- oracle11g 导入空表的办法
ORACLE 11G中有个新特性,当表无数据时,不分配segment,以节省空间 这样会出现导入导出数据库的时候报错,提示空表没有被还原,缺少表的情况 解决方法: 设置deferred_segment ...
- SQL语句查询结果额外加入一列序号自己主动添加
sqlserver 能够用row_number函数实现 例如以下: SELECT *,row_number() OVER(ORDER BY score(列名) DESC) AS rank FROM s ...
- Ellipse常用快捷键
Ctrl+m:视窗大小变化 Ctrl+F6:在打开的文件件进行切换 Ctrl+F7:在资源窗口间切换Ctrl+F8:在各种模式下进行切换 Ctrl+e:选择某个打开的文件Shift+home:整行选取 ...
- [week4]每周总结与工作计划
计算机网络 TAT 小白dp 28号还有一场 背单词 背马克思 python目标80% 熟悉coursera c++模版和 仿函数 人文修养 开学数据库,itercast的sql*2 itercast ...