动态规划(DP)算法
参考https://blog.csdn.net/libosbo/article/details/80038549
动态规划是求解决策过程最优化的数学方法。利用各个阶段之间的关系,逐个求解,最终求得全局最优解,需要确认原问题与子问题、动态规划状态、边界状态、边界状态结值、状态转移方程。
以下每个例题,注意分析迭代关系是怎么找到的。
一、爬楼梯leetcode70:
You are climbing a stair case. It takes n steps to reach to the top.
Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?
Note: Given n will be a positive integer.
Example 1:
Input: 2
Output: 2
Explanation: There are two ways to climb to the top.
1. 1 step + 1 step
2. 2 steps
Example 2:
Input: 3
Output: 3
Explanation: There are three ways to climb to the top.
1. 1 step + 1 step + 1 step
2. 1 step + 2 steps
3. 2 steps + 1 step
方法一:利用n个楼梯的步数,与n-1还有n-2之间的关系可以退出,f(n)==f(n-1)+f(n-2),相当于是直接考虑为n-1再上一步,和n-2直接上两步,不能考虑n-2有两种走法(一步一步,和一次两步,一步一步的会和n-1中的重复,导致算多了),最后不断的迭代直至可以n==1或者n==2,可以直接求出结果。
这个方法相当于是根据各个阶段之间的关系,列出迭代关系,并且写出临界解,从而结束递归的过程,否则将一直递归下去(所有的递归都是如此,如果没有边界条件提前结束递归,递归将不会停止)
这个时间复杂度是2^n相当于是一颗二叉树来着,leetcode显示time limit exceed
 int climbStairs(int n) {
         if(n==||n==){
             return n;
         }
         return climbStairs(n-)+climbStairs(n-);
    }
方法二:利用迭代来实现尾递归
由于方法一是利用了尾递归来实现算法,考虑采用迭代来实现递归,并且递归本身算法复杂度是要远远大于其对应的迭代循环算法复杂度的,所以考虑利用迭代来减少时间复杂度。两种方法的差别在于递归是从上往下算,迭代是从下往上算。
 class Solution {
 public:
     int climbStairs(int n) {
         vector<int>iteration(n+,); //initializition
         iteration[]=;
         iteration[]=;
         int i=;
         while(i<n+){
             iteration[i]=iteration[i-]+iteration[i-];
             i++;
         }
         return iteration[n];
     }
 };
二、抢劫犯问题leetcode198
You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night.
Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.
题目分析:这个题目的分析关键在于DP算法中状态转移方程的求解,也就是求解是迭代关系,可以发现对于第i个房间而言,如果不抢劫第i个房间那么就是i-1个的抢劫数目,如果抢劫第i个房间那么就是一定不能抢劫i-1个房间,相当于是i-2个房间的抢劫数目,两者时间的最大值即可,从而成功得出迭代关系。
注意自身在分析问题时的错误:
一:在无法的出迭代关系的情况下,没有考虑根据题意对可能的情况进行分类,注意两个题目都是进行了分类的讨论,才得以顺利的得出迭代关系,并且盲目的理解为迭代关系一定是两者之间的和,没有考虑到最大值得情况。
二:在考虑迭代关系时一定要思考如何引入第i-1个和第i-2个问题的解,这道题就是通过分类讨论,成功剥离出了i-1和i-2的情况;迭代关系的另一个要素是如何把i与i-1和i-2之间的关系找到
三:在考虑迭代关系时一定把i考虑成足够大,因为在代码实现过程中i很小的情况是直接给出的,直接赋值的(对于有dp数组而言),i很小的情况下只是考虑为边界条件,作为循环的起始或者是迭代的结束。所以考虑迭代关系时一定不要具体化i而是直接假设i足够大去考虑。
求取迭代关系的步骤:
1、根据题意分类讨论,分类讨论一定要达到引入i-1和i-2的解
2、挖掘i和i-1还有i-2之间的关系
3、边界条件确认
方法一、使用迭代法
 class Solution {
 public:
     int rob(vector<int>& nums) {
         if(nums.empty()) return ;
         if(nums.size()==) return nums[];
         vector<int>dp(nums.size(),);
         dp[]=nums[];
         dp[]=max(nums[],nums[]);
        for(int i=;i<nums.size();i++){
             dp[i]=max(dp[i-]+nums[i],dp[i-]);
         }
         return dp[nums.size()-];
     }
 };
方法二、使用递归算法
class Solution {
public:
    int rob(vector<int>& nums) {
        int size=nums.size();
        if(size==) return ;
        if(size==) return nums[];
        if(size==) return max(nums[],nums[]);
        vector<int>a1(nums.begin(),nums.end()-);
        vector<int>a2(nums.begin(),nums.end()-);
        return max(rob(a1),rob(a2)+nums[size-]);
    }
};
可以发现这种方法再次出现了time limit exceed,时间复杂度是O(2^n),以后不用再考虑递归的DP算法了,直接使用迭代,时间复杂度降低很多。
以上两道例题都是相似的,接下来的例题稍有不同,要重点理解。
三、最大子段和leetcode53(重点理解)
Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.
注意自身在分析问题时的错误:
在分析问题的时候没有灵活变通,直接考虑前i个连续子数组数组的最大值,将无法进行分类讨论,无法得到递归关系,并且在考虑递归关系时也是直接考虑了i与i-1还有i-2之间的关系,其实可以考虑为i与i-1的关系即可,只要是一种可以迭代出所有情况的关系即可。在不能够得出迭代关系的时候需要变通的考虑,改变dp数组的意义,不需要一步到位,只要保证可以通过dp数组得到最后的结果即可。
code:dp数组表示的是以第i个元素结尾的连续子数组的最大值,最后再寻找dp的最大值
 class Solution {
 public:
     int maxSubArray(vector<int>& nums) {
         int size=nums.size();
         vector<int>dp(size,);
         dp[]=nums[];
         for(int i=;i<size;i++){
             if(dp[i-]>) dp[i]=dp[i-]+nums[i];
             else dp[i]=nums[i];
         }
         int max1=dp[];
         for(int i=;i<size;i++){
             max1=max(max1,dp[i]);
         }
         return max1;
     }
 };
注意,这里判断dp[I-1]的正负来确定迭代关系(这是与之前两道例题都不同的地方),仔细理解根据num[i]正负来确定迭代关系的区别以及孰是孰非。
四、找零钱和leetcode322(非常经典的DP算法,稍复杂)——还未完全理解。。。
You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.
Example 1:
coins = [1, 2, 5], amount = 11
return 3 (11 = 5 + 5 + 1)
Example 2:
coins = [2], amount = 3
return -1.
Note:
You may assume that you have an infinite number of each kind of coin.
 class Solution {
 public:
     int coinChange(vector<int>& coins, int amount) {
         vector<int>dp(amount+,amount+);
         dp[]=;
         for(int i=;i<=amount;i++){
             for(int m:coins){
                 if(m<=i) dp[i]=min(dp[i],dp[i-m]+);
             }
         }
         if(dp.back()==amount+) return -;
             else return dp.back();
     }
 };
五、三角形——还未完全理解。。。
Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below.
For example, given the following triangle
[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
The minimum path sum from top to bottom is 11 (i.e., 2 + 3 + 5 + 1 = 11).
这道题目类似于第三题,都是对dp数组的意义进行转换,从而以退为进解决问题。
 class Solution {
 public:
     int minimumTotal(vector<vector<int>>& triangle) {
         int size=triangle.size();
         vector<vector<int>>dp(size,vector<int>(size,INT_MAX));
         dp[][]=triangle[][];
         for(int i=;i<size;i++){
             for(int j=;j<triangle[i].size();j++){
                 if(j==) dp[i][j]=dp[i-][j]+triangle[i][j];
                 if(j==triangle[i].size()-) dp[i][j]=dp[i-][j-]+triangle[i][j];
                 if(j!=&&j!=triangle[i].size()-) dp[i][j]=min(dp[i-][j-]+triangle[i][j],dp[i-][j]+triangle[i][j]);
             }
         }
         return *min_element(dp[size-].begin(),dp[size-].end());
     }
 };
动态规划(DP)算法的更多相关文章
- 动态规划——DP算法(Dynamic Programing)
		
一.斐波那契数列(递归VS动态规划) 1.斐波那契数列——递归实现(python语言)——自顶向下 递归调用是非常耗费内存的,程序虽然简洁可是算法复杂度为O(2^n),当n很大时,程序运行很慢,甚至内 ...
 - c++动态规划dp算法题
		
问题1:找硬币,换钱的方法 输入: penny数组代表所有货币的面值,正数不重复 aim小于等于1000,代表要找的钱 输出:换钱的方法总数 解法1:经典dp,空间复杂度O(n*aim) class ...
 - 算法-动态规划DP小记
		
算法-动态规划DP小记 动态规划算法是一种比较灵活的算法,针对具体的问题要具体分析,其宗旨就是要找出要解决问题的状态,然后逆向转化为求解子问题,最终回到已知的初始态,然后再顺序累计各个子问题的解从而得 ...
 - 0-1背包的动态规划算法,部分背包的贪心算法和DP算法------算法导论
		
一.问题描述 0-1背包问题,部分背包问题.分别实现0-1背包的DP算法,部分背包的贪心算法和DP算法. 二.算法原理 (1)0-1背包的DP算法 0-1背包问题:有n件物品和一个容量为W的背包.第i ...
 - 动态规划dp
		
一.概念:动态规划dp:是一种分阶段求解决策问题的数学思想. 总结起来就一句话:大事化小,小事化了 二.例子 1.走台阶问题 F(10):10级台阶的走法数量 所以:F(10)=F(9)+F(8) F ...
 - 最大子段和的DP算法设计及其效率测试
		
表情包形象取自番剧<猫咪日常> 那我也整一个 曾几何时,笔者是个对算法这个概念漠不关心的人,由衷地感觉它就是一种和奥数一样华而不实的存在,即便不使用任何算法的思想我一样能写出能跑的程序 直 ...
 - 华为笔试——C++平安果dp算法
		
题目:平安果 题目介绍:给出一个m*n的格子,每个格子里有一定数量的平安果,现在要求从左上角顶点(1,1)出发,每次走一格并拿走那一格的所有平安果,且只能向下或向右前进,最终到达右下角顶点(m,n), ...
 - C++数字三角形问题与dp算法
		
题目:数字三角形 题目介绍:如图所示的数字三角形,要求从最上方顶点开始一步一步下到最底层,每一步必须下一层,求出所经过的数字的最大和. 输入:第一行值n,代表n行数值:后面的n行数据代表每一行的数字. ...
 - dp算法之硬币找零问题
		
题目:硬币找零 题目介绍:现在有面值1.3.5元三种硬币无限个,问组成n元的硬币的最小数目? 分析:现在假设n=10,画出状态分布图: 硬币编号 硬币面值p 1 1 2 3 3 5 编号i/n总数j ...
 - dfs与dp算法之关系与经典入门例题
		
目录 声明 dfs与dp的关系 经典例题-数字三角形 - POJ 1163 题目 dfs思路 解题思路 具体代码 dp思路 解题思路 具体代码 声明 本文不介绍dfs.dp算法的基础思路,有想了解的可 ...
 
随机推荐
- Selenium2学习(五)-- SeleniumBuilder辅助定位元素
			
前言 福利来了,对于用火狐浏览器的小伙伴们,你还在为定位元素而烦恼嘛? 上古神器Selenium Builder来啦,哪里不会点哪里,妈妈再也不用担心我的定位元素问题啦!(但是也不是万能,基本上都能覆 ...
 - git相关操作(githug)
			
Level 15 restructure 关卡描述 你添加了一些文件到你的仓库,但现在知道你的项目需要进行调整.创建一个新的文件夹命名为“src”,使用git将所有的".html" ...
 - February 25 2017 Week 8 Saturday
			
Energy and persistence can conquer all things. 能量和毅力可以征服一切. I have the persistence, but it seems I a ...
 - WPF产生不重复的随机数
			
WPF产生不重复的随机数 在给定的区间范围(比如[50,99]),产生给定数量的随机数(不如10个),要求产生的随机数不重复首先给定变量min=50;max=99+1;//习惯与程序规定的编码不同所致 ...
 - 使用 Android 客户端向 Ruby on rails 构建的 Web Application 提交 HTTP GET 和 HTTP POST 请求
			
最近想弄个能访问 Internet 的 Android 应用,因为求快所以用了 Ruby on Rails 来提供 HTTP 资源.这方面的资料还是比较少的,所以把尝试的过程记录下来. 1 使用 Ru ...
 - 20165322 实验三 敏捷开发与XP实践
			
实验三 敏捷开发与XP实践 安装alibaba 插件,解决代码中的规范问题 根据老师的教程:打开Settings ->Plugins -> Browse repositories...在搜 ...
 - CF498D Traffic Jams in the Land
			
嘟嘟嘟 题面:有n条公路一次连接着n + 1个城市,每一条公路有一个堵塞时刻a[i],如果当前时间能被a[i]整除,那么通过这条公路需要2分钟:否则需要1分钟. 现给出n条公路的a[i],以及m次操作 ...
 - C++工程文件夹中的bin和obj文件夹有何用处?(补充多文件结构)
			
博主在使用Code::Blocks创建一个工程之后,正准备新建一个头文件,细心的博主发现,在工程文件夹中有两个子文件夹,分别是bin和obj.好奇心驱使下,想知道这两个文件夹用来干嘛的,网上搜了下,整 ...
 - js 注册控件的onclick事件
			
今天做了一个全选功能:1.点击全选,全部选中.选中状态再点击全选,全部取消选中2.点击成员,判断是否成员全部选中,true:全选为选中状态,false:全选为未选中状态. 使用js是比较麻烦的就是如何 ...
 - 【洛谷P3469】[POI2008]BLO-Blockade
			
BLO-Blockade 题目链接 若一个点为割点:统计出每个子树的大小,两两相乘再相加, 再加上n-1,为这个点与其他点的拜访数, 因为拜访是互相的,最后再乘二即可 若一个点不是割点:只有(n-1) ...