买卖股票的最佳时机
给定一个数组,它的第 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。
解题
动态规划的思路大多都是从后向前,先假设一个中间时刻i,再考虑当前时刻和前几次的联系,本题,当前时刻的股票收益,必然等于当前时刻的股票价格减去前面最便宜的股票价格,因此要定义两个变量,一个记录前面最便宜的股票价格,还有一个记录最大的股票收益。
状态转移矩阵:
a
=
m
a
x
(
a
,
p
r
i
c
e
s
[
i
]
−
m
i
n
)
a=max(a,prices[i]-min)
a=max(a,prices[i]−min)
class Solution {
public:
int maxProfit(vector<int>& prices) {
int cur = INT_MAX;
int res = 0;
for(int i=0;i<prices.size();i++)
{
cur = min(cur,prices[i]);
res = max(res,prices[i]-cur);
}
return res;
}
};
爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
- 1 阶 + 1 阶
- 2 阶
示例 2:
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
- 1 阶 + 1 阶 + 1 阶
- 1 阶 + 2 阶
- 2 阶 + 1 阶
解题
相同思路,到达当前台阶有两个方法,一个是从上上个跨两个台阶到,或者从上一个跨一个台阶到,因此状态方程为:
d
p
[
i
]
=
d
p
[
i
−
1
]
+
d
p
[
i
−
2
]
dp[i]=dp[i-1]+dp[i-2]
dp[i]=dp[i−1]+dp[i−2]
class Solution {
public:
int climbStairs(int n) {
if(n==0) return 0;
if(n==1) return 1;
if(n==2) return 2;
vector<int> dp(n,0);
dp[0]=1,dp[1]=2;
for(int i=2;i<n;i++)
{
dp[i] = dp[i-1]+dp[i-2];
}
return dp.back();
}
};
最大子序和
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
解题
先要分割成若干个子序,然后选取其中最大的一个的和。因此要定义两个数,一个要判断当前这个值是不是上一个子序列的,如果上一个子序列的和加上当前数比当前数小,那为什么还要加上上一个序列呢,直接从当前数开启新的序列不就是最大了吗,因此当前数作为下一个序列的第一个。而且注意还要有一个单独的变量记录最大值,因为每次更新会清除之前的和。
c
u
r
=
m
a
x
(
c
u
r
+
n
u
m
s
[
i
]
,
n
u
m
s
[
i
]
)
cur=max(cur+nums[i],nums[i])
cur=max(cur+nums[i],nums[i])
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int res = INT_MIN;
int cur = 0;
for(int i=0;i<nums.size();i++)
{
cur = max(cur+nums[i],nums[i]);
res = max(res,cur);
}
return res;
}
};
使用最小花费爬楼梯
数组的每个索引做为一个阶梯,第 i个阶梯对应着一个非负数的体力花费值 costi。
每当你爬上一个阶梯你都要花费对应的体力花费值,然后你可以选择继续爬一个阶梯或者爬两个阶梯。
您需要找到达到楼层顶部的最低花费。在开始时,你可以选择从索引为 0 或 1 的元素作为初始阶梯。
示例 1:
输入: cost = [10, 15, 20]
输出: 15
解释: 最低花费是从cost[1]开始,然后走两步即可到阶梯顶,一共花费15。
示例 2:
输入: cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1]
输出: 6
解释: 最低花费方式是从cost[0]开始,逐个经过那些1,跳过cost[3],一共花费6。
解题
到达当前楼梯的最小花费,取决于上上个楼梯到达,或者上个楼梯到达,取最小的就可以。
d
p
[
i
]
=
m
i
n
(
d
p
[
i
−
1
]
+
c
o
s
t
[
i
−
1
]
,
d
p
[
i
−
2
]
+
c
o
s
t
[
i
−
2
]
)
dp[i]=min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2])
dp[i]=min(dp[i−1]+cost[i−1],dp[i−2]+cost[i−2])
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
int len = cost.size();
if(len==0||len==1) return 0;
vector<int> dp(len+1,0);
for(int i=2;i<len+1;i++)
{
dp[i] = min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);
}
return dp.back();
}
};
打家劫舍
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。
示例 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 。
解题
和最大子序列类似,每个屋子都有偷和不偷两个选项,但是相连的就不能偷,因此当前这次的最大值来自于上上次偷和上上次不偷,上上次偷的话这次也必须偷,上上次不偷的话这次就不能偷也就是说这次的金额等于上一次的金额,因此要判断上上次偷和上上次不偷哪个大.
d
p
[
i
]
=
m
a
x
(
d
p
[
i
−
2
]
+
n
u
m
s
[
i
]
,
d
p
[
i
−
1
]
)
dp[i]=max(dp[i-2]+nums[i],dp[i-1])
dp[i]=max(dp[i−2]+nums[i],dp[i−1])
class Solution {
public:
int rob(vector<int>& nums) {
int len = nums.size();
if(len==0) return 0;
if(len==1) return nums[0];
if(len==2) return max(nums[0],nums[1]);
vector<int> dp(len,0);
dp[0] = nums[0];
dp[1] = max(nums[0],nums[1]);
for(int i=2;i<len;i++)
{
dp[i] = max(dp[i-2]+nums[i],dp[i-1]);
}
return dp.back();
}
};