02数组(动态规划)笔记

动态规划

动态规划:将一个大问题拆解成一个一个的子问题,将子问题的答案保存,递推到大问题解决。
解决问题的核心在于找到状态转移方程,与递推解决问题的方式有相似之处,通过下面几个例子体会一下。

剑指 Offer II 088. 爬楼梯的最少成本

数组的每个下标作为一个阶梯,第 i 个阶梯对应着一个非负数的体力花费值 cost[i](下标从 0 开始)。
每当爬上一个阶梯都要花费对应的体力值,一旦支付了相应的体力值,就可以选择向上爬一个阶梯或者爬两个阶梯。
请找出达到楼层顶部的最低花费。在开始时,你可以选择从下标为 0 或 1 的元素作为初始阶梯。
思路:
1)初始条件pd[0]=0,pd[1]=0;
2)边界条件,记有n个台阶,则最后达到台阶n+1终止,记为dp[n];
3)转移方程,上到第i个台阶的花费为dp[i]=dp[i-1]+cos(i-1)或者dp[i]=dp[i-2]+cos[i-2],取小即可;

class Solution {
    
    
public:
    int minCostClimbingStairs(vector<int>& cost) {
    
    
        int n=cost.size();//台阶长度为n,则共有n个台阶,到达n+1时方可到达顶层
        vector<int>dp(n+1);//创建长度为n+1的数组,从dp[0]到dp[n],其中dp[n]表示到达楼顶层的花费
        dp[0]=dp[1]=0;
        for(int i=2;i<n+1;i++){
    
    //注意从第二阶开始
            dp[i]=min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);
        }
        return dp[n];
    }
};

剑指 Offer II 089. 房屋偷盗

一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响小偷偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。给定一个代表每个房屋存放金额的非负整数数组 nums ,请计算 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
例如:屋子里面的钱为:[1 9 1 1 9],则偷到的金额为9+9=18.
动态规划问题关键在于寻找动态转移方程
1)假设小偷偷的前n户的金币总额为dp[n],n-1户的金币总额为dp[n-1];
2)当前小偷偷到第n+1户,倘若小偷偷第n户(金币数为num),则dp[n+1]=dp[n-1]+num,不偷则为dp[n+1]=dp[n];
3)仅需讨论此情况即可则dp[n+1]=max(dp[n-1]+num,dp[n]).
4)注意:不要过多讨论或者追溯dp[n-2],dp[n-3]…的情况,上式已经包括,属于重复子问题.
初始条件:金币数为零;边界条件:将所有户金币数遍历完即可。
代码实现如下:

class Solution {
    
    
public:
    int rob(vector<int>& nums) {
    
    
        int pre=0,cur=0,tmp;
        for(int i=0;i<nums.size();i++){
    
    
            tmp=cur;
            cur=max(pre+nums[i],cur);
            pre=tmp;
        }
        return cur;
    }
};

剑指 Offer II 090. 环形房屋偷盗

一个专业的小偷,计划偷窃一个环形街道上沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。给定一个代表每个房屋存放金额的非负整数数组 nums ,请计算 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。
解决方法相同,问题 在于增加一个初始条件,偷第一个不偷最后一个,或者偷第一个不偷最后一个

class Solution {
    
    
public:
    int rob(vector<int>& nums) {
    
    
        if(nums.size()==0){
    
    
            return 0;
        }
        if(nums.size()==1){
    
    
            return nums[0];
        }
        int pre1=0,pre2=0,cur1=0,cur2=0,tmp;
        for(int i=0;i<nums.size()-1;i++){
    
    
            tmp=cur1;
            cur1=max(pre1+nums[i],cur1);
            pre1=tmp;
        }
        for(int i=1;i<nums.size();i++){
    
    
            tmp=cur2;
            cur2=max(pre2+nums[i],cur2);
            pre2=tmp;
        }
        return max(cur1,cur2);
    }
};

剑指 Offer II 091. 粉刷房子

假如有一排房子,共 n 个,每个房子可以被粉刷成红色、蓝色或者绿色这三种颜色中的一种,你需要粉刷所有的房子并且使其相邻的两个房子颜色不能相同。当然,因为市场上不同颜色油漆的价格不同,所以房子粉刷成不同颜色的花费成本也是不同的。每个房子粉刷成不同颜色的花费是以一个 n x 3 的正整数矩阵 costs 来表示的。
例如,costs[0][0] 表示第 0 号房子粉刷成红色的成本花费;costs[1][2] 表示第 1 号房子粉刷成绿色的花费,以此类推。
请计算出粉刷完所有房子最少的花费成本。
1)当前房子花费,如果为红色则有dp[i][0]=min(dp[i-1][1],dp[i-1][2])+costs[i][0],同理也有dp[i][1],dp[i][2];
2)最终为dp[i][?]=min(dp[i][0],dp[i][1],dp[i][2]);
3)注意初始时的花费dp[0][0]=costs[0][0]…
4)注意边界条件

class Solution {
    
    
public:
    int minCost(vector<vector<int>>& costs) {
    
    
        int n=costs.size();
        vector<vector<int>>dp(n,vector<int>(3, 0));
        //vector<vector<int>> table(size1, vector<int>(size2, 0));二维数组初始化代码
        dp[0][0]=costs[0][0];dp[0][1]=costs[0][1];dp[0][2]=costs[0][2];
        for(int i=1;i<n;i++){
    
    
            dp[i][0]=min(dp[i-1][1]+costs[i][0],dp[i-1][2]+costs[i][0]);
            dp[i][1]=min(dp[i-1][0]+costs[i][1],dp[i-1][2]+costs[i][1]);
            dp[i][2]=min(dp[i-1][0]+costs[i][2],dp[i-1][1]+costs[i][2]);
        }
        return min(min(dp[n-1][0],dp[n-1][1]),dp[n-1][2]);
    }
};

总结

动态规划关键在于找到状态转移方程,同时注意开始的初始条件和终止条件即边界条件。找状态方程可以利用穷举分析自底向上,但不要陷入无穷的追溯前序或者后序的状态中,及时理清每一个子问题

引用:

https://cloud.tencent.com/developer/article/1817113
https://leetcode-cn.com/problems/Gu0c2T/
https://leetcode-cn.com/problems/PzWKhm
https://leetcode-cn.com/problems/GzCJIP
https://leetcode-cn.com/problems/JEj789

猜你喜欢

转载自blog.csdn.net/weixin_42370166/article/details/123476258