算法之回溯&动态规划&贪心

回溯使用场景:求出所有可能的解。

List result;
void backtrack(路径,选择列表){
	if(满足结束条件){
		result.add(路径);
		return;
	}
	for(选择:选择列表){// 遍历集合中的元素
		做选择;
		backtrack(路径,选择列表);
		撤销选择;
	}
}

动态规划使用场景:寻求最优解。

#初始化 base case
dp[0][0][...] = base
#进行状态转移
for 状态1 in 状态1的所有取值:
	for 状态2 in 状态2的所有取值:
		for ...
			dp[状态1][状态2][...] = 求最值(选择1,选择2...)

当然动态规划的问题也可以通过回溯算法暴力求解。


1.动态规划

动态规划【Dynamic Programming,DP】三要素:通过分析发现题目存在重叠子问题【剪枝优化】以及最优子结构则可以确定为动态规划问题。

  • 最优子结构:动态规划将一个复杂的问题分解成若干个子问题,通过综合子问题的最优解来得到原问题的最优解。通俗理解就是不同子问题组合后都能解决原问题,但是需要最优的子问题组合。
  • 重叠子问题:动态规划会将每个求解过的子问题的解记录【dp数组】下来,这样当下一次碰到同样的子问题时,就可以直接使用之前记录的结果,而不是重复计算。(虽然动态规划使用这种方式来提高计算效率,但不能说这种做法就是动态规划的核心)。
  • 有时需要所有子问题综合结果【回溯、暴力也能解决】但有时需要提取子问题的最优组合。

大概包含以下4个概要:重叠子问题(不具有独立性,互相依赖)最优子结构状态转移方程求最值问题

解析思路:明确「状态」、明确「选择」、明确dp函数/数组的定义、明确base case。 

难点:

  1. 首先需要给每个状态赋予实际意义。
  2. 根据状态推断出状态转移方程【如果一维数组无法解决尝试二维数组】
  3. 确认初始化值,即状态转移方程中dp元素的初始值。不管暴力求解还是动态规划求解,初始值是终止嵌套流程的关键条件。

解决方式:

  • 从初始化条件开始由内向外或者由下至上,逐层向外依次求终解。
  • 从外层向内或者由上到下逐层嵌套寻找初始化条件,最终反向或者由内到外求终解。

实际上,子问题分解是一种通用的算法思路,在分治、动态规划、回溯中的侧重点不同。
‧ 分治算法递归地将原问题划分为多个相互独立的子问题,直至最小子问题,并在回溯中合并子问题的解,最终得到原问题的解。
‧ 动态规划也对问题进行递归分解,但与分治算法的主要区别是,动态规划中的子问题是相互依赖的,在分解过程中会出现许多重叠子问题。
‧ 回溯算法在尝试和回退中穷举所有可能的解,并通过剪枝避免不必要的搜索分支。原问题的解由一系列决策步骤构成,我们可以将每个决策步骤之前的子序列看作一个子问题。

实际上,动态规划常用来求解最优化问题,它们不仅包含重叠子问题,还具有另外两大特性:最优子结构、无
后效性。


2.回溯算法

根据目标元素使用方式分为两部分:重复使用【钱币兑换问题】 vs 单次使用。

同时对比不同解决方案的结果:分情况的状态转移方程,取不同情况下结果的最优值。

你可能感兴趣的:(算法,动态规划)