给定一个长度为 N 的数组,数组中的第 i个数字表示一个给定股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成两笔交易。
注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
输入格式
第一行包含整数 N,表示数组长度。
第二行包含 N 个不大于 10000 的正整数,表示完整的数组。
输出格式
输出一个整数,表示最大利润。
数据范围
1≤N≤1e5
输入样例1:
8 3 3 5 0 0 3 1 4
输出样例1:
6
分析这道题目,这里就给大家带来动态规划的做法了, 由于这里限制只能买卖两次,所以这里咱们就要设置新的变量参数:1 第一次买入 ; 2 第一次卖出 ; 3 第二次买入 ; 4 第二次卖出。
所以根据这个我们就可以推出状态转移方程:
dp[i][1] = max(dp[i - 1][1] ,-a[i]);
dp[i][2] = max(dp[i - 1][2] , dp[i - 1][1] + a[i]);
dp[i][3] = max(dp[i - 1][3] , dp[i - 1][2] - a[i]);
dp[i][4] = max(dp[i - 1][4] , dp[i - 1][3] + a[i]);
这里就不仔细讲解了,提供一个思路,可以结合我的前几篇文章,提到了思路。
那么完整代码展示:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 101010;
int n;
int dp[N][4];
int a[N];
int main()
{
cin >> n;
for(int i = 0;i < n;i++) cin >> a[i];
dp[0][1] = -a[0];
dp[0][3] = -a[0];
for(int i = 1;i < n;i++)
{
dp[i][1] = max(dp[i - 1][1] ,-a[i]);
dp[i][2] = max(dp[i - 1][2] , dp[i - 1][1] + a[i]);
dp[i][3] = max(dp[i - 1][3] , dp[i - 1][2] - a[i]);
dp[i][4] = max(dp[i - 1][4] , dp[i - 1][3] + a[i]);
}
cout << dp[n - 1][4];
}
其实看过以及学完01背包滚动数组的知识,我们不难发现这里的转台转移是一层一层推出来的,i由i - 1推出来的,所以这里给大家带来了滚动数组的写法:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 101010;
int n;
//1 第一次买入 ; 2 第一次卖出 ; 3 第二次买入 ; 4 第二次卖出
int dp[N];
int a[N];
int main()
{
cin >> n;
for(int i = 0;i < n;i++) cin >> a[i];
dp[1] = -a[0];
dp[3] = -a[0];
for(int i = 1;i < n;i++)
{
dp[1] = max(dp[1] , dp[0] - a[i]);
dp[2] = max(dp[2] , dp[1] + a[i]);
dp[3] = max(dp[3] , dp[2] - a[i]);
dp[4] = max(dp[4] , dp[3] + a[i]);
}
cout << dp[4];
}
既然学到这里,那么如果不是两次呢?如果可以买卖k次呢?那么看题吧:
给定一个长度为 N 的数组,数组中的第 i 个数字表示一个给定股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润,你最多可以完成 k 笔交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。一次买入卖出合为一笔交易。
输入格式
第一行包含整数 N 和 k,表示数组的长度以及你可以完成的最大交易笔数。
第二行包含 N 个不超过 10000 的正整数,表示完整的数组。
输出格式
输出一个整数,表示最大利润。
数据范围
1≤N≤1e5
1≤k≤100输入样例1:
3 2 2 4 1
输出样例1:
2
输入样例2:
6 2 3 2 6 5 0 3
输出样例2:
7
这道题就可以无非跟上面买卖两次一个道理,如果买卖两次我用1 2 3 4来分别表示,那么买卖k次不就是1 2 3 ... 2k,所以只需要在此基础上加入一个for循环枚举一下k就可以了。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 101010;
int n,k;
int dp[N][120];
int a[N];
int main()
{
cin >> n >> k;
for(int i = 0;i < n;i++) cin >> a[i];
for(int i = 1;i <= 2 * k;i += 2) dp[0][i] = -a[0]; //初始化,在奇数的情况下是买的状态,所以会给其赋值
for(int i = 1;i < n;i++)
{
for(int j = 1;j <= 2 * k;j += 2)
{
dp[i][j] = max(dp[i - 1][j] , dp[i - 1][j - 1] - a[i]); //买
dp[i][j + 1] = max(dp[i - 1][j + 1] , dp[i - 1][j] + a[i]); //卖
}
}
cout << dp[n - 1][2 * k];
}
这里注意,因为这里咱们的二维数组开的相对来说会爆栈,所以这里咱们用滚动数组优化一下代码:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 101010;
int n,k;
int dp[200]; //k≤100
int a[N];
int main()
{
cin >> n >> k;
for(int i = 0;i < n;i++) cin >> a[i];
for(int i = 1;i <= 2 * k;i += 2) dp[i] = -a[0]; //初始化
for(int i = 1;i < n;i++)
{
for(int j = 1;j <= 2 * k;j += 2)
{
dp[j] = max(dp[j] , dp[j - 1] - a[i]); //买
dp[j + 1] = max(dp[j + 1] , dp[j] + a[i]); //卖
}
}
cout << dp[2 * k];
}
好了本期内容到这里了,感谢收看,记得三连支持一下,下期继续分享股票买卖问题。