动态规划思想以及常见应用
2018-03-30 18:30
375 查看
一 动态规划的基本思想以及和贪婪算法、分治法的比较
动态规划的基本思想:将复杂问题进行分解,通过求解小规模子问题反推出原问题的结果。动态规划适合求解多阶段决策问题的最优解(可以简单理解为有状态转换的阶段性问题)。这些问题必须满足最优化原理和子问题的无后向性。最优化原理:不管之前的决策是否最优,但是一定要保证从现在开始的决策是在之前决策基础上的最优决策。
无后向性原理:当各个子阶段的子问题确定以后,对于某个特定阶段的子问题来说,它之前的各个阶段的子问题的决策只影响当前阶段的决策,而对该阶段之后的决策不产生影响。即每个阶段的决策只受之前决策的影响,不影响之后各阶段的决策。
动态规划与贪婪算法以及分治法的比较:
三者都需要对原问题进行分解,分解为需要求解的子问题。分治法求解的子问题是独立的,每个问题的求解模式一样,分别求解再合并就是原问题的解。而贪婪算法只考虑当前状态,并依据贪婪法则取得局部最优解,直到扩展到原来的问题。动态规划的子问题需要满足最优化原理和无后向性原理。
解决动态规划问题的关键是找到状态转换方程。下面介绍一些常见的应用:
一 最长公共子序列
问题:对两个子序列S和T,求解S和T最大公共子序列的长度。例如 S:ABCDEF;T:ACDF。则最大子序列为ACDF。
分析:假设a[i][j]表示S中下标[0,i]和T中下标[0,j]之间序列的最大公共子序列。分析可知:
代码如下:int maxCommonSubsequence(const string& s1, const string& s2)
{
vector<vector<int>> a(s1.size() + 1, vector<int>(s2.size() + 1, 0));
for (int i = 1; i <= s1.size(); i++)
{
for (int j = 1; j <=s2.size(); j++)
{
if (s1[i - 1] == s2[j - 1])
{
a[i][j] = 1 + a[i - 1][j - 1];
}
else
{
a[i][j] = max(a[i-1][j],a[i][j-1]);
}
}
}
return a[s1.size()][s2.size()];
}
假如对上面的最长子序列加一个限制,改为最长连续公共子序列。又该如何求解呢?
代码如下:int maxConsecutiveSubsequence(const string& s1, const string& s2)
{
vector<vector<int>> a(s1.size() + 1, vector<int>(s2.size() + 1, 0));
int res = 0;
for (int i = 1; i <= s1.size(); i++)
{
for (int j = 1; j <=s2.size(); j++)
{
if (s1[i - 1] == s2[j - 1])
{
a[i][j] = 1 + a[i - 1][j - 1];
res = max(res, a[i][j]);
}
else
{
a[i][j] = 0;
}
}
}
return res;
}
二 最长递增子序列
问题:给定一个数组,求最长递增子序列长度。比如a[10]={4,0,2,6,1,3,5,9,6,7,8},则最长递增子序列为:0,1,3,5,6,7,8。分析:假设数组为V,记从[0,k]之间的最长递增子序列长度为result[k],则有:
result[k]=1+max{result[i]|0<=i<=k-1&&(V[i]<=V[k])}
代码如下:int max_inc_seq(const vector<int> & s)
{
int res = 0;
vector<int> result(s.size(), 0);
result[0] = 1;
for (int i = 1; i < s.size(); i++)
{
result[i] = 1;
for (int j = 0; j < i; j++)
{
if ((s[j]<=s[i]) && result[i]<(result[j] + 1))
{
result[i] = result[j] + 1;
res = max(res, result[i]);
}
}
}
return res;
}
三 0-1背包问题
问题描述:有N件物品和一个承重为C的背包,每件物品的重量是Wi,价值为Pi,在不超过背包容量的前提下,求那几件物品可以使背包中价值最大?
分析:假设p[i][w]表示前i个物品的最优背包解,背包容量是w。分析状态转换关系:
代码如下:int pack(const vector<int> &w, const vector<int> &p, int W, vector<int> &every_element)
{
vector<vector<int>> a(w.size() + 1, vector<int>(W + 1, 0));
for (int i = 1; i <= w.size(); i++)
{
for (int j = 1; j <= W; j++)
{
if (w[i - 1]<= j)
{
a[i][j] =(((a[i - 1][j])>(p[i - 1] + a[i - 1][j - w[i - 1]])) ?
(a[i - 1][j]) : (p[i - 1] + a[i - 1][j - w[i - 1]]));
}
else
{
a[i][j] = a[i - 1][j];
}
}
}
for (int i =w.size(), j =p.size(); i >= 1 && j >= 1;)
{
if (a[i][j] ==a[i - 1][j])
{
i--;
}
if (j >= w[i - 1] && a[i][j]==(p[i - 1] + a[i - 1][j - w[i - 1]]))
{
every_element.push_back(p[i - 1]);
j -= w[i - 1];
i--;
}
}
return a[w.size()][W];
}
个人感觉动态规划问题的关键在于找到一个状态转换方程,或者说是一个递推关系。没有一个固定的模式,必须自己分析出来。
同时利用数组存储中间的运算变量,可以提高效率。
相关文章推荐
- 100道动态规划——9 UVA 10723 Cyborg Genes LCS的应用以及有趣的题干
- leetcode 第四题:动态规划思想的应用
- 如何动态加载控件以及插件编程思想(C#)
- 黑马程序员_学习笔记2交通灯系统中面向对象思想的理解以及工厂模式的应用
- 100道动态规划——8 UVA 1631 Locker 递推,状态的定义以及状态转移方程
- 动态规划之Fib数列类问题应用
- Atitit 军事学 之 军事思想学与打猎学总结以及在软件行业中的应用 attilax著 1. 军事思想在软件行业技术开发中的想通之处 1 1.1. 软件开发本质上是一种作战,敌人是时间与费用成本
- 简单动态规划两题(思想一样)
- LAMP环境的搭建以及基于php动态网站的应用
- Fibonacci数列的动态规划思想
- Advanced Fruits(合并字符串+最长公共子序列应用)hdu1503 +动态规划
- 动态规划_最大子序列和问题以及最大子序列问题
- 动态规划:凑硬币(中级:动态规划思想体会)
- Alphacode (动态规划思想)POJ - 2033 HDU - 1508
- 常见的动态规划问题分析与求解
- leetcode 第32题:动态规划思想与堆栈的灵活使用
- (C#)利用反射动态调用类成员、动态加载控件以及插件编程思想
- LSM树由来、设计思想以及应用到HBase的索引
- 动态规划的思想
- LA 3720 highway(利用动态规划思想,将复杂度急降,9ms)