回溯法和动态规划的一般模板
Posted limancx
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了回溯法和动态规划的一般模板相关的知识,希望对你有一定的参考价值。
1.回溯法
回溯法的基础题目类型就是子集树和排列树,掌握最基础的模板,那么其它的题目都可以在此上变形得到,子集树即如下图所示:
同一个元素不能被选入多次,子集树的算法模板为
1 void Backtrack(int t) { //t 表示当前是树的第t层,即对集合 S 中的第 t 个元素进行判断 2 if (t > n) 3 output(x); //大于S中总的元素个数 ,遍历完成 4 else 5 for (int i = 0; i < = l; i++) { // 两种可能 加入或者不加入到解集合 6 x[t] = i; 7 if (Constraint(t) && Bound(t)){ //满足约数条件 8 Backtrack(t + 1); //对 t+1 层进行判断 9 } 10 } 11 }
还有就是排列树,排列树即
这种,即一个元素可以多次被选择,排列树的模板为
1 void Backtrack(int t) { //t 表示集合 S 的第 t 个元素 2 if (t > n) 3 output(x); 4 else 5 for (int i = t; i <= n; i++) { //第t 个元素与其后面的所有元素进行交换位置 6 swap(x[t], x[i]); 7 if (constraint(t) && bound(t)){ 8 backtrack(t + 1); 9 } 10 swap(x[t], x[i]); 11 } 12 }
在回溯法的问题中,只要在这两种基础的模板上变形即可。
2.动态规划
动态规划问题相对来说有一点难度,这里可以帮助给出动态规划的一般思路:
首先确定问题符合动态规划问题的一般条件,即无后效性、最优子结构性质和重叠子问题性质。一般我们可以这样理解,如果你想构造一个一维或二维dp数组,如果当前要决策的变量dp[i]的值跟后面的决策无关(无后效性),仅仅和前面的某个或者某几个dp[j](其中j为小于i的值)有关,且dp[j]也是之前第j个决策变量决策时的最优选择(最优子结构性质),同时由决策dp[j]时的状态转移到dp[i]的状态的转移策略和dp[i]转移到dp[k](其中k大于i)的状态转移策略是相同的,那么这时候就适合用动态规划算法。
动态规划算法和贪心算法的不同之处在于,贪心算法往往只跟当前状态有关,而动态规划是不仅仅和当前状态有关,并且和之前的某些状态有关的。
当确定可以使用动态规划算法后,就要确定这个dp数组中每一个元素可能的取值,即可能有的决策方案或者状态值;
然后动态规划算法的最重要的就是提取状态转移方程,一个可以参考的做法是考虑某个决策变量/状态dp[i]和前面的哪些状态有关,即先找到dp[i]和dp[j]、dp[m]、dp[n]这些先前状态的位置,也即j、m、n可能的取值,然后要确定j,m,n怎么由i得到,比如j可能是i-1,m可能是i-2,n可能是i-dp[i-1],此时已经确定dp[i]和哪些状态有关系了。接下来,就是确定怎么由dp[j],dp[m],dp[n]得到dp[i],也就是状态转移方程,一般就是根据当时的状态dp[j]以及经过了几个阶段后的影响change[j],然后求出每个有关的状态+影响,即dp[j]+change[j],dp[m]+change[m],以及dp[n]+change[n]多个取最优。
当对动态规划转移方程仍然不清晰时,可以手动采用表格法尝试一下模拟的过程。
动态规划的一般模板是
1 int dynamicProgramming(int *state,int size) 2 { 3 int dp[size]; 4 dp[0] = initState; 5 dp[1] = initState; 6 for(int i = 2; i < size;++i) 7 { 8 int a = dp[i-1] + f(state[i],state[i-1] ); 9 int b = dp[i-2] + f(state[i],state[i-1] ); 10 int c = dp[i-n] + f(state[i],state[i-n] ); 11 dp[i] = best(a,b,c); 12 } 13 return dp[size-1]; 14 }
以上是关于回溯法和动态规划的一般模板的主要内容,如果未能解决你的问题,请参考以下文章
六中常用算法设计:穷举法分治法动态规划贪心法回溯法和分支限界法