股票买卖IV(LeetCode)

题目描述

给定一个长度为 N N N 的数组,数组中的第 i i i 个数字表示一个给定股票在第 i i i 天的价格。

设计一个算法来计算你所能获取的最大利润,你最多可以完成 k k k 笔交易。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。一次买入卖出合为一笔交易。

输入格式
第一行包含整数 N N N k k k,表示数组的长度以及你可以完成的最大交易笔数。

第二行包含 N N N 个不超过 10000 的正整数,表示完整的数组。

输出格式
输出一个整数,表示最大利润。

数据范围
1 ≤ N ≤ 1 0 5 , 1≤N≤10^5, 1N105,
1 ≤ k ≤ 100 1≤k≤100 1k100

输入样例1:

3 2
2 4 1

输出样例1:

2

输入样例2:

6 2
3 2 6 5 0 3

输出样例2:

7

样例解释

样例1:在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 。

样例2:在第 2 天 (股票价格 = 2) 的时候买入,在第 3 天 (股票价格 = 6) 的时候卖出, 这笔交易所能获得利润 = 6-2 = 4 。随后,在第 5 天 (股票价格 = 0) 的时候买入,在第 6 天 (股票价格 = 3) 的时候卖出, 这笔交易所能获得利润 = 3-0 = 3 。共计利润 4+3 = 7.


首先,应列出该问题下的状态机模型:

请添加图片描述

那么在任意一天中,应该只存在 “手中无股”“手中有股” 两种状态。

f[i][j][1/0]如图中含义的基础下,各种状态转移的具体含义是:

注*: 这里的状态转移均是考虑第 i − 1 i - 1 i1到第 i i i天或者第 j − 1 j - 1 j1笔交易到第 j j j笔交易时的状态。

  • 手中有股(1) —> 手中无股(0):说明此时进行了股票抛售,得到的利润应是+ price[i],由于该事件发生后才能算作一笔完整的交易,所以它们在一个共同的j下完成: f ( i , j , 0 ) = f ( i − 1 , j , 1 ) + p r i c e [ i ] f(i,j,0)=f(i-1,j,1)+price[i] f(i,j,0)=f(i1,j,1)+price[i]
  • 手中有股(1) —> 手中有股(1):说明该股票没动,也就不会获得利润+ 0,也就是交易数还处在第 j j j笔,对应地: f ( i , j , 1 ) = f ( i − 1 , j , 1 ) f(i,j,1)=f(i-1,j,1) f(i,j,1)=f(i1,j,1)
  • 手中无股(0) —> 手中有股(1):说明该股票买入了,买入就需要花钱,所以利润为负值- price[i],那么买入该股票后交易笔数也就到了新的一轮,如果当前交易的是第 j j j笔交易,那就应该由 j − 1 j-1 j1笔交易转移而来: f ( i , j , 1 ) = f ( i − 1 , j − 1 , 0 ) − p r i c e [ i ] f(i,j,1)=f(i-1,j-1,0)-price[i] f(i,j,1)=f(i1,j1,0)price[i]
  • 手中无股(0) —> 手中无股(0):说明该股票没动,也就不会获得利润+ 0,也就是交易数还处在第 j j j笔,对应地: f ( i , j , 0 ) = f ( i − 1 , j , 0 ) f(i,j,0)=f(i-1,j,0) f(i,j,0)=f(i1,j,0)

决策: max

在初始状态下, f ( i , 0 , 1 ) f(i,0,1) f(i,0,1)这类状态是不合法的,为什么呢?因为 f ( i , 0 , 1 ) f(i,0,1) f(i,0,1)的释义是:第 i i i 天持有第0笔交易的股票,而股票完成笔数必须是≥1的,也即要想持有股票,必须至少处在第1笔交易;所以为了不让这类非法状态转移到其它状态,就需要同对它赋一些不合法的初值,可以是 − ∞ -∞ ,这样max必定不会选中它。
但是 f ( i , 0 , 0 ) f(i,0,0) f(i,0,0)这些状态是合法的,就为0,表示没开始进行股票交易时不持股,其实它也就是上图中状态机的“入口”

最后,由于 k k k只是表示的最多交易的笔数,不一定就是完成到了 k k k笔交易,所以需要枚举一下各笔交易下得到的利润最大值。


C++代码

#include <iostream>
#include <cstring>
using namespace std;

const int N = 1e5 + 10, M = 110;

int n, k;
int p[N], f[N][M][2];      

int main(){
    memset(f, -0x3f, sizeof f);
    ios :: sync_with_stdio(false);
    cin >> n >> k;
    for(int i = 1;i <= n;i ++)      cin >> p[i];
    for(int i = 0;i <= n;i ++)      f[i][0][0] = 0;
    
    for(int i = 1;i <= n;i ++)
        for(int j = 1;j <= k;j ++){
            //每个状态分别max
            f[i][j][0] = max(f[i - 1][j][0], f[i - 1][j][1] + p[i]);
            f[i][j][1] = max(f[i - 1][j][1], f[i - 1][j - 1][0] - p[i]);
        }
    
    int ans = 0;
    for(int i = 1;i <= k;i ++)      ans = max(ans, f[n][i][0]);
    cout << ans << endl;
    
    return 0;
}

观察一下其实可以对代码做等价变形,由于状态转移式子的右边均是i - 1维的,这就联想到了0-1背包里的滚动数组优化,把i的这一层的存储删去,f就优化到了二维。

二维Code

#include <iostream>
#include <cstring>
using namespace std;

const int N = 1e5 + 10, M = 110;

int n, k;
int p[N], f[M][2];      

int main(){
    memset(f, -0x3f, sizeof f);
    ios :: sync_with_stdio(false);
    cin >> n >> k;
    for(int i = 1;i <= n;i ++)      cin >> p[i];
    f[0][0] = 0;
    
    for(int i = 1;i <= n;i ++)
        for(int j = k;j >= 1;j --){
            //每个状态分别max
            f[j][0] = max(f[j][0], f[j][1] + p[i]);
            f[j][1] = max(f[j][1], f[j - 1][0] - p[i]);
        }
    
    int ans = 0;
    for(int i = 1;i <= k;i ++)      ans = max(ans, f[i][0]);
    cout << ans << endl;
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值