动态规划(DP)---- 买卖股票(3)

给定一个长度为 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 
#include 
#include 
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 
#include 
#include 
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 
#include 
#include 
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 
#include 
#include 
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];
}

好了本期内容到这里了,感谢收看,记得三连支持一下,下期继续分享股票买卖问题。

 

你可能感兴趣的:(动态规划,动态规划,算法)