算法题--月饼的最佳买卖时机

836 阅读2分钟

我正在参加中秋创意投稿大赛,详情请看:中秋创意投稿大赛

之前就看到了这个活动,看到有人写了画月亮、爬月饼销量,不想重复,也没想好写什么就一直搁置了。今天上午看到一个公众号里面有关月饼的算法题,又想起了这个活动,就写一下我自己的理解吧。

题目

牛牛买了几盒公司月饼,过了几天,再打开内网一看,月饼的价格已经从公司的发售价78,涨到了150。短短几天,就翻了倍,这勾起了牛牛的好奇心,开始关注🥮价格。

盯了两天,发现价格每天都有波动,牛牛算是明白了,这鹅厂的月饼也变成了抢手货,有黄牛在中间捣腾,作为一个合格的程序员,当然联想到了自己的业务——这要是放在计算机里,黄牛的最佳买入卖出时机如何实现?

输入
[7,1,5,3,6,4]

输出
5

思路

首先本地有1个隐含条件:买入时间必须小于等于卖出时间,即不允许做空,如果允许做空的话,只要找到数组的最小值和最大值就好了。
原文给出了暴力、贪心、动态规划3种思路循序渐进,但是总感觉有些关键点没明确点出来,看评论里面也确实有同学没理解,这里详细说说动态规划的思路。
个人认为动态规划里面最难的是定义状态,这个比较依靠经验。本题定义的状态是:数组dp表示以某天为卖点的最大获利,例如dp[3]就是以第4天价格卖出可以获利的最大值(下标为3,实际是第4天)。状态转义方程是 dp[i] = max(dp[i-1]+p[i]-p[i-1], 0)。
接下来说对这个状态转义方程的理解,为什么有这个公式。
其实获利只跟买入价格和卖出价格有关,dp[i]是以i下标的价格卖出的最大获利,dp[i-1]是以i-1为下标的最大获利,想象一种最简单的情况,对于数组p[0]~p[i-1]和p[0]~p[i],如果他们的买入价格是一样的,只是卖出的价格由p[i-1]变成了p[i],那么显然dp[i] = dp[i-1]+p[i]-p[i-1],买入的价格是min(p[0]~p[i-1])。那什么时候会不一样呢,就是p[i] < min(p[0]~p[i-1])的时候,这时候应该将p[i]作为最低的买入价格,而此时,只能选择在p[i]卖出,所以,这种情况下,获利是0。
原文的图还挺清晰,不再另外画图了: 月饼买卖时机.png

Java版本代码

public static int yuebing(int[] p) {
    int len = p.length;
    int[] dp = new int[len];
    dp[0] = 0;
    int result = 0;
    for (int i = 1; i < len; i++) {
        dp[i] = Math.max(dp[i-1] + p[i] - p[i-1], 0);
        result = Math.max(result, dp[i]);
    }
    return result;
}