[动态规划]---打家劫舍

最近在网上看到一个题,叫做“打家劫舍”,题目来源:https://leetcode-cn.com/

遵纪守法,人人有责!

本题使用的是动态规划的思想。关于什么是动态规划,参考的相关资料和相关博客总结了一下,感谢各位提供的资料。

动态规划

动态规划(Dynamic Programming,DP)是运筹学的一个分支,是求解决策过程最优化的过程。

在现实生活中,有一类活动的过程,由于它的特殊性,可将过程分成若干个互相联系的阶段,在它的每一阶段都需要作出决策,从而使整个过程达到最好的活动效果。因此各个阶段决策的选取不能任意确定,它依赖于当前面临的状态,又影响以后的发展。当各个阶段决策确定后,就组成一个决策序列,因而也就确定了整个过程的一条活动路线.这种把一个问题看作是一个前后关联具有链状结构的多阶段过程就称为多阶段决策过程,这种问题称为多阶段决策问题。在多阶段决策问题中,各个阶段采取的决策,一般来说是与时间有关的,决策依赖于当前状态,又随即引起状态的转移,一个决策序列就是在变化的状态中产生出来的,故有“动态”的含义,称这种解决多阶段决策最优化的过程为动态规划方法。(百度百科)

通俗点:对于某类问题,可以拆分成若干个子问题,按照顺序求解子问题的解,前一个子问题的解,为后一个问题提供相关信息,每个子问题的解不能随意确定。依次下去,就能形成一条解决问题的子集解集合。总之一句话就是,大事化小,小事化了。

1.术语:

阶段:把所给求解问题的过程恰当地分成若干个相互联系的阶段,以便于求解,过程不同,阶段数就可能不同.描述阶段的变量称为阶段变量。在多数情况下,阶段变量是离散的,用k表示。此外,也有阶段变量是连续的情形。如果过程可以在任何时刻作出决策,且在任意两个不同的时刻之间允许有无穷多个决策时,阶段变量就是连续的。

状态:状态表示每个阶段开始面临的自然状况或客观条件,它不以人们的主观意志为转移,也称为不可控因素。在上面的例子中状态就是某阶段的出发位置,它既是该阶段某路的起点,同时又是前一阶段某支路的终点。

无后效性:我们要求状态具有下面的性质:如果给定某一阶段的状态,则在这一阶段以后过程的发展不受这阶段以前各段状态的影响,所有各阶段都确定时,整个过程也就确定了。换句话说,过程的每一次实现可以用一个状态序列表示,在前面的例子中每阶段的状态是该线路的始点,确定了这些点的序列,整个线路也就完全确定。从某一阶段以后的线路开始,当这段的始点给定时,不受以前线路(所通过的点)的影响。状态的这个性质意味着过程的历史只能通过当前的状态去影响它的未来的发展,这个性质称为无后效性。

决策:一个阶段的状态给定以后,从该状态演变到下一阶段某个状态的一种选择(行动)称为决策。在最优控制中,也称为控制。在许多问题中,决策可以自然而然地表示为一个数或一组数。不同的决策对应着不同的数值。描述决策的变量称决策变量,因状态满足无后效性,故在每个阶段选择决策时只需考虑当前的状态而无须考虑过程的历史。

策略:由每个阶段的决策组成的序列称为策略。对于每一个实际的多阶段决策过程,可供选取的策略有一定的范围限制,这个范围称为允许策略集合。

允许策略集合中达到最优效果的策略称为最优策略。

上述术语比较专业化,还是需要根据具体的问题来理解。

  1. 多阶段决策,意味着问题可以分解成子问题,子子问题,。。。,也就是说问题可以拆分成多个子问题进行求解。
  2. 最优子结构,在自下而上的递推过程中,我们求得的每个子问题一定是全局最优解,既然它分解的子问题是全局最优解,那么依赖于它们解的原问题自然也是全局最优解。
  3. 自下而上,怎样才能自下而上的求出每个子问题的最优解呢,可以肯定子问题之间是有一定联系的,即迭代递推公式,也叫「状态转移方程」,要定义好这个状态转移方程, 我们就需要定义好每个子问题的状态(DP 状态),那为啥要自下而上地求解呢,因为如果采用像递归这样自顶向下的求解方式,子问题之间可能存在大量的重叠,大量地重叠子问题意味着大量地重复计算,这样时间复杂度很可能呈指数级上升,所以自下而上的求解方式可以消除重叠子问题。
  • 最优子结构,状态转移方程,重叠子问题就是动态规划的三要素,这其中定义子问题的状态与写出状态转移方程是解决动态规划最为关键的步骤,状态转移方程如果定义好了,解决动态规划就基本不是问题了。

2.基本结构

多阶段决策问题中,各个阶段采取的决策,一般来说是与时间有关的,决策依赖于当前状态,又随即引起状态的转移,一个决策序列就是在变化的状态中产生出来的,故有“动态”的含义,称这种解决多阶段决策最优化问题的方法为动态规划方法。

备注:感觉像这种比较难以理解的问题,需要多刷题,找到问题中的相似点,题目做多了,以后遇到这类问题就能很快的找到解题思路,光看概念之类的东西真的很难理解,这个只是个人理解。

打家劫舍题解:

此题解参考来源:leetcode

题目中规定,第一个房间和最后一个房间是连接在一起的,所以整个数组理论上是一个环形结构,所以选择第一个数,就不能选择最后一个数。选择最后一个数,就不能选择第一个数。所以最后归结为:

1.在不选第一个数的情况下nums[ n - 1 ],获得的最大金额为p1;

2.在不选择最后一个数的情况下num[ 1 ],获得的最大金额为p2;

然后只需要比较max(p1,p2),即可获得最大金额。

状态转移方程:f(n) = max( nums[n] + f(n-2) , f(n-1) )

代码如下:

class Solution {
public:
    int rob(vector<int>& nums) {
		int n = nums.size();
        if(n==0) 
            return 0;
        if(n == 1) 
            return nums[0];
        int firstMoney = subRob(nums,0,n-1);
        int lastMoney = subRob(nums,1,n);
        return max(firstMoney,lastMoney);
    }

     int subRob(vector<int>& nums,int start,int end){
        int prev = 0;
        int cur = 0;
        for(int i=start;i<end;i++){
            int temp = cur;
            cur = max(nums[i]+prev, cur);
            prev = temp;
        }
        return cur;
     }
};

我们发现 dp[n]只与 dp[n-1]和 dp[n-2]有关系,因此我们可以设两个变量 cur和 prev 交替记录,将空间复杂度降到 O(1)。

prev表示前n-1的值,cur表示n的值,prev的值也就是cur未进行下一次计算的值。nums[i]+prev表示当前房间的金额加上前n-1房间中的最大金额。

(后续有新的理解再更新...)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值