众(自)所(己)周(瞎)知(说) DP在NOIP应用的很多
动态规划算法通常基于一个递推公式及一个或多个初始状态。 当前子问题的解将由上一次子问题的解推出。使用动态规划来解题只需要多项式时间复杂度, 因此它比回溯法、暴力法等要快许多。
现在让我们通过一个例子来了解一下DP的基本原理。
首先,我们要找到某个状态的最优解,然后在它的帮助下,找到下一个状态的最优解。 ——by Hawstein
什么是动态规划?
和分治法一样,动态规划(dynamic programming)是通过组合子问题而解决整个问题的解。
分治法是将问题划分成一些独立的子问题,递归地求解各子问题,然后合并子问题的解。
动态规划适用于子问题不是独立的情况,也就是各子问题包含公共的子子问题。
此时,分治法会做许多不必要的工作,即重复地求解公共的子问题。动态规划算法对每个子问题只求解一次,将其结果保存起来,从而避免每次遇到各个子问题时重新计算答案。
适用范围
最优性原理体现为问题的最优子结构特性。当一个问题的最优解中包含了子问题的最优解时,则称该问题具有最优子结构特性。
最优性原理是动态规划的基础。任何一个问题,如果失去了这个最优性原理的支持,就不可能用动态规划设计求解。
1.问题中的状态满足最优性原理。
2.问题中的状态必须满足无后效性。
所谓无后效性是指:“下一时刻的状态只与当前状态有关,而和当前状态之前的状态无关,当前状态是对以往决策的总结”。
——by 剑仙
对于它的设计 我感觉应该有两种方法:
自顶向下(直接跑记忆化搜索):基本上对应着递归函数实现,从大范围开始计算,要注意不断保存中间结果,避免重复计算。
自底向上(递推):从小范围递推计算到大范围。
DP有特别重要的两点:
1.写出方程。
2.确定边界。
举个栗子
一个人每次只能走一层楼梯或者两层楼梯,问走到第80层楼梯一共有多少种方法。
当我第一次看到这个题目的时候 感觉特别难 无从下手 仔细想想 还是有思路的
这个题是经典的一维动态规划。爬楼梯数目其实是一个斐波拉契数列。
假定f[i] 表示是爬到第i层的方法,那么f[i] = f[i-1] + f[i-2] //第i层的方法数目等于第i-1层数目加上第i-2层数目。
初值:f[0] = 1, f[1] = 1。 //最开始没有爬和第一层的方法数目为1.
输出:f[n] 爬到第n层的方法数目。
上代码:
#include <iostream> using namespace std; int f[10001] = {0};//初始化一下 避免后面出故障 int main() { int num; cin>>num;//可以配合顶置帖子的那个读入优化写 本题无所谓 f[1] = 1;//爬到第一层只有一种方法:一次爬一个 f[2] = 2;//爬到第二层有两种方法:一次爬一个、一次爬两个 for(int i = 3; i <= num; i++) { f[i] = f[i-1] + f[i-2];//递推公式 } cout<<f[num]<<endl;//到第sum层 return 0; }
有时间继续补充(逃