股票买卖IV

股票买卖IV

题目描述

image-20210220150404434


核心思路

这一题我们可以用状态机来解决

状态表示:

  • f [ i ] [ j ] [ 0 ] f[i][j][0] f[i][j][0]表示前i天买卖了j次(即交易了j次),当前手中无票,能够获取的最大利润。
  • f [ i ] [ j ] [ 1 ] f[i][j][1] f[i][j][1]表示前i天买卖了j次(即交易了j次),当前手中有票,能够获取的最大利润。

带权的有向图:表示状态边权表示转移的价值增量

image-20210220150746135

对于第i天的状态0来说, 有两种可能:

  • 前i-1天买卖了j次导致手中已经无票了,但是第i天啥也不干,并没有买或卖,那么第i天的无票状态就是从前i-1天的无票状态转移过来的
  • 前i-1天买卖了j次,但是手中还要票,然在第j次交易中把票卖出去了,导致第i天手中无票,因此第i天的无票状态就是从前i-1天的有票状态转移过来的

所以无票状态转移方程为: f [ i ] [ j ] [ 0 ] = m a x ( f [ i − 1 ] [ j ] [ 0 ] , f [ i − 1 ] [ j ] [ 1 ] + w [ i ] ) f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1]+w[i]) f[i][j][0]=max(f[i1][j][0],f[i1][j][1]+w[i])

对于第i天的状态1来说,有两种可能:

  • 前i-1天交易了j次然后手里还有票,第i天啥也不干,并没有买或卖,那么第i天的无票状态就是从前i-1天的有票状态转移过来的
  • 前i-1天是无票了,但是第i天买票了,我们规定只要买票了,就是一次新的交易,所以第i天的有票状态是从前i-1天的无票状态交易了j-1次转移过来的

所以有票的状态转移方程为: f [ i ] [ j ] [ 1 ] = m a x ( f [ i − 1 ] [ j ] [ 1 ] , f [ i − 1 ] [ j − 1 ] [ 0 ] ) f[i][j][1]=max(f[i-1][j][1],f[i-1][j-1][0]) f[i][j][1]=max(f[i1][j][1],f[i1][j1][0])

为什么状态转移方程不能是下面代码,即卖的时候才算做了一次交易,为什么是把买了股票就算作一次新的交易呢?

f[i][j][0] = max(f[i - 1][j - 1][1] + w[i], f[i - 1][j][0]);
f[i][j][1] = max(f[i - 1][j][0] - w[i], f[i - 1][j][1]);

终究要回归到状态转移的起点第一支股票只有买,和不买这两个操作,一定不可能是卖和不卖的这两个操作,因此第一支股票如果买入时,必须按照一次交易处理。

对于第i天的无票状态0,有两种情况:

  • 要么在第i天之前我就已经买过票了,然后并没有卖掉,到了第i天,我卖出去了,这样完成了买和卖的过程,这样就完成了一次交易,也就是说我已经完成了第j-1次交易。因此如果下次从状态0变到了状态1,说明我又新买了一只股票,那就意味着此时我是新开始了一笔交易,因为上一次交易已经结束了,也就是说此时我正在进行第j次交易,买了一支股票但还没有卖出,因此只是正在进行第j次交易,还没有结束第j次交易。
  • 要么前i-1天就无票,则从第i-1天回溯,第i-1天无票,第i-2天无票,一直到第1天无票,也就是说我从一开始就处于无票状态,到了第i天我仍没有买票。因此,假设我们把卖出票看作是一笔新的交易,那么对于这种情况来说,我第一天都还没有买入票,怎么可能会卖出票,怎么可以算是一次交易呢。

image-20210220154310934

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mAS9Mdle-1643125741768)(C:\Users\3Code_Love\AppData\Roaming\Typora\typora-user-images\image-20210220154601746.png)]

把三维压缩成二维

image-20210220155503985


代码

三维数组

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=100010,K=110;
//f[i][j][0]表示前i天买卖了j次(即交易了j次),当前手中无票,能够获取的最大利润。
//f[i][j][1]表示前i天买卖了j次(即交易了j次),当前手中有票,能够获取的最大利润。
int f[N][K][2];
int w[N];   //w[i]表示第i天的股票价格
int main()
{
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
    scanf("%d",&w[i]);
    //处理非法边界,第0天不可能有交易,也就是不可能会有票,因此赋值为负无穷大
    //由于f数组是全局的了,所以对于第0天无票的情况即f[0][j][0]已经使用全局的0了,因此不需要写了
    //这里是处理第0天有票的情况
    for(int j=0;j<=k;j++)
    f[0][j][1]=-0x3f3f3f3f;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=k;j++)//不能超过k次交易
        {
            f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1]+w[i]);     //无票
            f[i][j][1]=max(f[i-1][j][1],f[i-1][j-1][0]-w[i]);   //有票
        }
    }
    // int res=0;
    // for(int j=0;j<=k;j++)
    // res=max(res,f[n][j][0]);
    // printf("%d\n",res);
    cout <<f[n][k][0]<<endl;
    return 0;
}

二维数组

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=100010,K=110;
int f[K][2];
int w[N];   //w[i]表示第i天的股票价格
int main()
{
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
    scanf("%d",&w[i]);
    //处理非法边界,第0天不可能有交易,也就是不可能会有票,因此赋值为负无穷大
    //由于f数组是全局的了,所以对于第0天无票的情况即f[j][0]已经使用全局的0了,因此不需要写了
    //这里是处理第0天有票的情况
    for(int j=0;j<=k;j++)
    f[j][1]=-0x3f3f3f3f;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=k;j++)//不能超过k次交易
        {
            f[j][0]=max(f[j][0],f[j][1]+w[i]);     //无票
            f[j][1]=max(f[j][1],f[j-1][0]-w[i]);   //有票
        }
    }
    cout <<f[k][0]<<endl;
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卷心菜不卷Iris

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值