[leetcode初级算法]动态规划总结

爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。

  1. 1 阶 + 1 阶
  2. 2 阶

示例 2:
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。

  1. 1 阶 + 1 阶 + 1 阶
  2. 1 阶 + 2 阶
  3. 2 阶 + 1 阶

c++代码

方法一:递归
但是显示超时了

class Solution {
public:
    int climbStairs(int n) {
        if(n==1)
            return 1;
        if(n==2)
            return 2;
        return climbStairs(n-1)+climbStairs(n-2);

    }
};

方法二:循环
毕竟还是比递归快的

class Solution {
public:
    int climbStairs(int n) {
        if(n==1)
            return 1;
        if(n==2)
            return 2;
        int ret=0;
        int pre=2;
        int prepre=1;
        for(int i=3;i<=n;i++)
        {
            ret=pre+prepre;
            prepre=pre;
            pre=ret;
        }
        return ret;
        

    }
};

买卖股票的最佳时机

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。

注意你不能在买入股票前卖出股票。

示例 1:

输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。
示例 2:

输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

c++代码

方法一:常规方法
两次循环,找到相差最大的。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int num=0;
        int max=0;
        for(int i=0;i<prices.size();i++)
        {
            for(int j=i+1;j<prices.size();j++)
            {
                int tmp=prices[j]-prices[i];
                if(tmp>max)
                    max=tmp;
            }
        }
        return max;
    }
};

方法二
遍历一遍数组就可以。buy选择遍历过的数组中的最小值,然后每次计算当前值和这个最小值之间的差值做为利润,然后每次选较大的利润来更新

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int buy=INT_MAX;
        int res=0;
        for(int i=0;i<prices.size();i++)
        {
            buy=min(buy,prices[i]);
            res=max(res,prices[i]-buy);
        }
        return res;
    }
};

最大子序和

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例:

输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
进阶:

如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。

思路:

设n[i]为以第i个元素结尾且和最大的连续子数组。假设对于元素i,所有以它前面的元素结尾的子数组的长度都已经求得,那么以第i个元素结尾且和最大的连续子数组实际上,要么是以第i-1个元素结尾且和最大的连续子数组加上这个元素,要么是只包含第i个元素,即n[i]
= max(n[i-1] + nums[i], nums[i])。可以通过判断n[i-1] + nums[i]是否大于nums[i]来做选择,而这实际上等价于判断n[i-1]是否大于0。由于每次运算只需要前一次的结果,因此并不需要像普通的动态规划那样保留之前所有的计算结果,只需要保留上一次的即可,因此算法的时间和空间复杂度都很小

c++代码

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int sum=nums[0];
        int n=nums[0];
        for(int i=1;i<nums.size();i++) {
            if(n>0)
                n+=nums[i];
            else 
                n=nums[i];
            if(sum<n)
            	sum=n;
        }
        return sum;

    }
};

打家劫舍

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。

示例 1:

输入: [1,2,3,1]
输出: 4
解释: 偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 2:

输入: [2,7,9,3,1]
输出: 12
解释: 偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。

思路:

动态规划,dp[i]表示到第号房间偷窃到的最大金额。那么状态转移方程怎么写呢

我们先拿一个简单的例子来分析一下,比如说nums为{3, 2, 1, 5},那么我们来看我们的dp数组应该是什么样的,首先dp[0]=3没啥疑问,再看dp[1]是多少呢,由于3比2大,所以我们抢第一个房子的3,当前房子的2不抢,所以dp[1]=3,那么再来看dp[2],由于不能抢相邻的,所以我们可以用前面的前面的一个的dp值加上当前的房间值,和当前房间的前面一个dp值比较,取较大值当做当前dp值,所以我们可以得到
状态转移方程dp[i] = max(num[i] + dp[i - 2], dp[i - 1]),
由此看出我们需要
初始化dp[0]和dp[1],其中dp[0]即为num[0],dp[1]此时应该为max(num[0], num[1])

c++代码

class Solution {
public:
    int rob(vector<int>& nums) {
        if(nums.empty())
            return 0;
        if(nums.size()==1)
            return nums[0];
        vector<int> dp={nums[0],max(nums[0],nums[1])};
        for(int i=2;i<nums.size();i++)
        {
            dp.push_back(max(dp[i-2]+nums[i],dp[i-1]));
        }
        return dp.back();
    }
};

你可能感兴趣的:(笔试题)