本系列将分享十一月在marscode上遇到的比较有意思的题目供同学们交流分享。
本题也将用到一个很常用的算法——动态规划。
问题描述
小R近期表现出色,公司决定以股票的形式给予奖励,并允许他在市场上进行交易以最大化收益。给定一个数组,数组中的第 i 个元素代表第 i 天的股票价格。小R需要设计一个算法来实现最大利润。
股票交易规则如下:
- 小R可以多次买卖股票,但在买入新的股票前必须卖出之前的股票。
- 每次卖出股票后存在一天的冷冻期,在冷冻期内小R不能购买股票。
你的任务是帮助小R计算出在遵守交易规则的情况下能够获得的最大利润。
stocks: 一个整数列表,表示连续几天内的股票价格。
测试样例
样例1:
输入:
stocks = [1, 2]
输出:1
样例2:
输入:
stocks = [2, 1]
输出:0
样例3:
输入:
stocks = [1, 2, 3, 0, 2]
输出:3
样例4:
输入:
stocks = [2, 3, 4, 5, 6, 7]
输出:5
样例5:
输入:
stocks = [1, 6, 2, 7, 13, 2, 8]
输出:12
问题分析
- 我们已知一个整数数组
stocks,其中每个元素代表对应天数的股票价格。 - 需要在遵循特定交易规则(可多次买卖,但买入前须卖出且每次卖出后有一天冷冻期)的情况下,设计算法求出能获得的最大利润。
关键算法:
动态规划
- 本题中会用到变化二位数组,适合用动态规划来解决。我们可以定义几个状态来表示在不同情况下所能获得的最大利润。
- 设
dp[i][0]表示第i天结束时,处于未持有股票且不在冷冻期的最大利润。 - 设
dp[i][1]表示第i天结束时,处于持有股票的最大利润。 - 设
dp[i][2]表示第i天结束时,处于未持有股票且在冷冻期的最大利润。
还需要弄明白 状态转移关系
-
对于
dp[i][0]:- 它可以由两种情况转移而来。一种是前一天就处于未持有股票且不在冷冻期的状态,即
dp[i - 1][0];另一种是前一天处于未持有股票且在冷冻期的状态,即dp[i - 1][2]。所以dp[i][0] = max(dp[i - 1][0], dp[i - 1][2])。
- 它可以由两种情况转移而来。一种是前一天就处于未持有股票且不在冷冻期的状态,即
-
对于
dp[i][1]:- 它只能由前一天处于未持有股票且不在冷冻期的状态,然后在第
i天买入股票转移而来。因为买入股票会花费资金,所以利润要减去当天的股票价格,即dp[i][1] = dp[i - 1][0] - stocks[i]。
- 它只能由前一天处于未持有股票且不在冷冻期的状态,然后在第
-
对于
dp[i][2]:- 它只能由前一天处于持有股票的状态,然后在第
i天卖出股票转移而来。因为卖出股票会获得资金,所以利润要加上当天的股票价格,即dp[i][2] = dp[i - 1][1] + stocks[i]。
- 它只能由前一天处于持有股票的状态,然后在第
创建动态规划数组:
int** dp = (int**)malloc(n * sizeof(int*));
for (int i = 0; i < n; i++) {
dp[i] = (int*)malloc(3 * sizeof(int));
}
初始化、填充动态规划数组:
dp[0][0] = 0;
dp[0][1] = -stocks[0];
dp[0][2] = 0;
// 填充动态规划数组
for (int i = 1; i < n; i++) {
dp[i][0] = dp[i - 1][0] > dp[i - 1][2]? dp[i - 1][0] : dp[i - 1][2];
dp[i][1] = dp[i - 1][0] - stocks[i];
dp[i][2] = dp[i - 1][1] + stocks[i];
}
// 计算最大利润
int max_profit_value = dp[n - 1][0] > dp[n - 1][2]? dp[n - 1][0] : dp[n - 1][2];
// 释放动态规划数组内存
for (int i = 0; i < n; i++) {
free(dp[i]);
}
free(dp);
经过上述两步操作,就成功地动态创建了一个二维数组 dp,它有 n 行 3 列,可以用来存储 int 类型的元素,并且可以根据程序的实际需求灵活地指定行数 n,而不像静态二维数组那样在编译时就必须确定数组的大小。
需要注意的是,在使用完动态分配的内存后,要记得使用 free 函数释放这些内存,以避免内存泄漏。
以上便是实现题述功能的关键步骤,我们也认识到二维数组同指针结合后高效的定位、存取能力,也同样体现了C语言的最大特色——指针、内存管理,也是C区别于其他高级语言的特点之一,同学们还应多多学习,努力掌握更多的、高效的有意思的算法,并加以运用到刷题实践中去,在一次一次的反思、巩固中拥抱更强大的自己!
附警句:你所写下的每一行代码都是铺向未来的路。