开始做动态规划部分的内容了,这部分有四个题,所以就对半开吧。
做这些题目的时候,有那么一些感觉像是回到了小时候做奥数的感觉,反正都是难。
第一题:(爬楼梯)
假设你正在爬楼梯。需要 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 阶
> 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/climbing-stairs
> 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
第二题:买卖股票的最佳时机
给定一个数组,它的第 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。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
我的题解(1)
这题居然判定为简单题,看来是我太菜了。
我倒也不是做不出来,只是我的算法无法处理过大的数据,哎。
我是这样想的:既然有n个台阶,假设两步走的次数为k,则一步走的次数就是n-2k,那么将这些两步走的次数穿插在一步走的情况当中,就是一个排列组合问题。
那就将排列组合整理成一个函数,对k进行递归并求和2,便是最后的结果。
于是代码就出来了:
//二代版本
int twostep(int n,int two)
{
int temp = 1;
int i = n - two;
int del = 1;
for (; i > n - 2 * two; i--)
{
temp *= i;
while (temp % del == 0 && del <= two)
{
temp /= del;
del++;
}
}
while (del <= two)
{
temp /= del;
del++;
}
return temp;
}
int climbStairs(int n)
{
int step = n / 2;
int ret = 1;
while (step > 0)
{
ret += twostep(n, step);
step--;
}
return ret;
}
一代版本是全乘完再统一除。
然后发现当n到35的时候就扛不住了,换了二代版本。没想到还是抗不过45。
其实如果将除数放在数组里面做三代版本是可以的,但是那样就太肥了。。。
最后,我接受了现实,人外有人,那就看看别人怎么做的。
斐波那契数列。。。
斐波那契解法
int climbStairs(int n) {
if(n==1||n==2)
return n;
int n1=1,n2=2;
for(int i=3;i<=n;i++)
{
int temp=n1+n2;
n1=n2;
n2=temp;
}
return n2;
}
原来这是一个找规律的题目啊。。。
我的题解(2)
上次做的那题,可以多次买卖,这次只能单次,那波峰波谷法就不好用了。
因为当天下降可能是“虚假下降”,接下来会有反弹。
其实虚假下降也好判断。但是吧,困扰我的是:
“过度虚假下降”:就是后面会反弹,但是这个下降力度已经超过了之前积攒下来的盈利,出现这种情况,应该认为之前的天数都不存在,从波谷重头来过。
其实这个情况单独处理也并不是什么很难的事情,但是把这些单独处理都没什么问题的糅杂在一起的时候,那就很麻烦了。
前面失败了之后,我又想起了最初那个被掐断的小火苗:干脆递归传入当前最小值,遍历找利益最大处。然后,过了。
int maxProfit(vector<int>& prices) {
if (prices.size() < 1)
return 0;
int the_min= prices[0];
int ret = 0;
for (int i = 1; i < prices.size(); i++) {
ret = max(ret, prices[i] - the_min);
the_min = min(the_min, prices[i]);
}
return ret;
}
其实我只是尝试一下,因为一开始掐灭这个想法也不是没有道理的。
因为最后买入点可不一定会是波谷,卖出点是不是波峰倒是无所谓,关键就是那个买入点。
但是,当它奇迹般的通过的时候,我突然想通了,ret和不止有the_min一个变量在控制,所以the_min是不是波谷对ret并没有绝对的影响。
有点绕,没绕出来打盘王者冷静冷静。一定是可以绕出来的。
官方题解(1)
方法三:动态规划(斐波那契数列)
算法
不难发现,这个问题可以被分解为一些包含最优子结构的子问题,即它的最优解可以从其子问题的最优解来有效地构建,我们可以使用动态规划来解决这一问题。
第 ii 阶可以由以下两种方法得到:
在第 (i-1) 阶后向上爬一阶。
在第 (i−2) 阶后向上爬 2 阶。
所以到达第 ii 阶的方法总数就是到第 (i-1)(i−1) 阶和第 (i-2)(i−2) 阶的方法数之和。
令 dp[i]dp[i] 表示能到达第 ii 阶的方法总数:
dp[i]=dp[i-1]+dp[i-2]
dp[i]=dp[i−1]+dp[i−2]
作者:LeetCode
链接:https://leetcode-cn.com/problems/climbing-stairs/solution/pa-lou-ti-by-leetcode/
来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
官方题解(2)
方法二:一次遍历
算法
假设给定的数组为:[7, 1, 5, 3, 6, 4]
如果我们在图表上绘制给定数组中的数字,我们将会得到:
我们来假设自己来购买股票。随着时间的推移,每天我们都可以选择出售股票与否。那么,假设在第 i 天,如果我们要在今天卖股票,那么我们能赚多少钱呢?
显然,如果我们真的在买卖股票,我们肯定会想:如果我是在历史最低点买的股票就好了!太好了,在题目中,我们只要用一个变量记录一个历史最低价格 minprice,我们就可以假设自己的股票是在那天买的。那么我们在第 i 天卖出股票能得到的利润就是 prices[i] - minprice。
因此,我们只需要遍历价格数组一遍,记录历史最低点,然后在每一天考虑这么一个问题:如果我是在历史最低点买进的,那么我今天卖出能赚多少钱?当考虑完所有天数之时,我们就得到了最好的答案。
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/solution/121-mai-mai-gu-piao-de-zui-jia-shi-ji-by-leetcode-/
来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
public class Solution {
public int maxProfit(int prices[]) {
int minprice = Integer.MAX_VALUE;
int maxprofit = 0;
for (int i = 0; i < prices.length; i++) {
if (prices[i] < minprice)
minprice = prices[i];
else if (prices[i] - minprice > maxprofit)
maxprofit = prices[i] - minprice;
}
return maxprofit;
}
}
> 作者:LeetCode-Solution
> 链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/solution/121-mai-mai-gu-piao-de-zui-jia-shi-ji-by-leetcode-/
> 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
总结
动手前,还是先想清楚吧。。。。。。
永远不要节省整理思绪的时间,不然,在实现功能的时候都会让你还。