买卖股票的最佳时机问题 C++

发布于:2025-03-29 ⋅ 阅读:(34) ⋅ 点赞:(0)

一,买卖股票的最佳时机

【题目描述】

有一个价格数组prices,表示第i天的股票价格为prices[i],整个过程只能买入股票和卖出股票一次。并且买入股票必须再卖出股票之前。

 【思路】

贪心

我们可以遍历一次数组,遍历到prices[i],表示在第i天卖出股票,为了使利益最大,我们需要在前i-1天中选择股票价格最低的一天买入。可以使用一个变量leftMin来记录前i-1天中,股票最低的价格。那么此时的最大利益就是prices[i]-leftMin。

#include <iostream>
using namespace std;

const int N=1e5+10;
int prices[N];
int n;
int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
    cin>>prices[i];
    //记录前i-1天中股票最低的价格
    int leftMin=0x3f3f3f3f;
    int ans=0;
    for(int i=1;i<n;i++)
    {
        leftMin=min(leftMin,prices[i-1]);
        ans=max(ans,prices[i]-leftMin);
    }
    cout<<ans<<endl;
    return 0;
}

动态规划

dp[n][2]:dp[i][0]表示第i天未持有股票的最大利益,dp[i][1]表示第i天持有股票的最大利益。

状态转移方程:

dp[i][0]两种情况:1,前一天仍未购买 2,之前购买,现在卖出

dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i])

dp[i][1]两种情况:1,之前买了,现在也不卖 2,之前没买,现在卖了

dp[i][1]=max(dp[i-1][1],-prices[i])

初始值dp[0][0]=0。

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

const int N=1e5+10;
int prices[N];
int dp[N][2];
int n;
int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
    cin>>prices[i];
    //记录前i-1天中股票最低的价格
    dp[0][0]=0;
    dp[0][1]=-prices[0];
    for(int i=1;i<n;i++)
    {
        dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i]);
        dp[i][1]=max(dp[i-1][1],-prices[i]);
    }
    cout<<dp[n-1][0];
    return 0;
}

二,买卖股票的最佳时机2

【问题描述】

本题与上题相似,区别是本题可以进行多次买卖。

【思路】

贪心

 对于股票数组prices,根据prices[i]的大小,prices数组可能由以下几个部分组成:

某个区间prices[i]呈现上升趋势,某个区间prices[i]呈现下降趋势,某个区间prices[i]呈现上升趋势。

而为了求出最大利益,我们只需每个上升的区间,只需在开始买入,结束卖出即可。

#include <iostream>
using namespace std;

const int N=1e5+10;
int n;
int arr[N];
int main()
{
    cin>>n;
    int ret=0;
    for(int i=0;i<n;i++)
    cin>>arr[i];
    for(int i=1;i<n;i++)
    if(arr[i]>arr[i-1])
    ret+=arr[i]-arr[i-1];

    cout<<ret<<endl;
    return 0;
}

动态规划

同理 ,dp[n][2]。

dp[i][0]:表示第i天不持有股票的最大利益。

dp[i][1]:表示第i天持有股票的最大利益。

分析:

dp[i][0]:第i天不持有股票,那么该状态可以来自两方面。1,dp[i-1][0],前一天不持有股票,今天也不持有,最大利益不变;2,dp[i-1][1]+prices[i]前一天持有股票,今天将股票卖出。所以,可得:dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i])。

dp[i][1]:第i天持有股票。前一天持有股票,今天继续 持有,也就是dp[i-1][1];前一天不持有股票,几天持有股票(买入股票),也就是dp[i-1][0]-prices[i]。所以,可得:dp[i][0]=max(dp[i-1][1],dp[i-1][0]-prices[i])。

#include <iostream>
using namespace std;

const int N=1e5+10;
int n;
int dp[N][2];
int prices[N];
int main()
{
    cin>>n;
    int ret=0;
    for(int i=1;i<=n;i++)
    cin>>prices[i];
    dp[1][1]=-prices[1];
    dp[1][0]=0;
    for(int i=2;i<=n;i++)
    {
        dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i]);
        dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i]);
    }


    cout<<dp[n][0]<<endl;
    return 0;
}

三,买卖股票的最佳时机3

【问题描述】

 本题中,我们最多只能完成2笔交易。并且第一笔交易完成后才能进行第二笔交易。

【思路】

动态规划

首先看几个例子:

prices = [3,3,5,0,0,3,1,4],对于这种情况,我们需要交易两次,可以获得的最大利益是6。

prices = [1,2,3,4,5],对于这种情况,最大利益是在第一天买入,在最后一天卖出。只交易一次。

prices = [7,6,4,3,1] ,对于这种情况,最大利益是0,我们不进行交易,交易次数为0。

所以最大利益可能是在交易2次,交易1次或者交易0次的情况下。


首先定义状态表示:dp[i]表示,第i天结束时,最大的利益。

但是对于第i天,有多种状态,比如第i天处于买入状态,或者第i天处于卖出状态。还有第i天的时候,交易了多少次。所以我们使用这种一维的dp是无法表示的。

更改后的状态表示:

注意:买入状态表示我们手里有股票,卖出状态表示手里没有股票,处于可交易的状态。

f[i][j]:表示第i天结束后,处于买入状态,交易次数为j,此时的最大利益。

g[i][j]:表示第i天结束后,处于卖出状态,交易次数为j,此时的最大利益。

状态转移方程的分析:

f[i][j]:表示第i天处于买入状态,状态可以来自两方面:1,前一天处于买入状态,今天仍处于买入状态,即f[i-1][j]。2,前一天处于卖出状态,今天买入股票,处于买入状态,即g[i-1][j]-prices[i]。

所以f[i][j]=max(f[i-1][j],g[i-1][j]-prices[i])。

g[i][j]:表示第i天处于卖出状态,状态可以来自两方面:1,前一天处于卖出状态,今天仍处于卖出 状态,即 g[i-1][j]。2,前一天处于买入状态,今天卖出股票,处于卖出状态。此时就是完成一笔交易。即f[i-1][j-1]+prices[i]。注意这里交易次数的变化,前一天的交易次数为j-1,今天卖出股票后,交易次数++,交易次数变为j。

所以g[i][j]=max(g[i-1][j],f[i-1][j-1]+prices[i])。j-1可能会出现负数,需要判断。


 

f表和g表的初始化问题:

f[0][0]=-prices[0],第一天买入状态,交易0次,最大利益为-prices[0]。

g[0][0]=0,第一天手里没有股票,交易0次,处于卖出状态,最大利益为0。

根据状态表示的定义:f[0][j],j从1开始,表示交易j次。但是第一天无法交易j次,所以这些位置 是没有意义的,为了不影响计算,可以初始化为无穷小。

同理,g[0][j],j从1开始,表示交易j次。但是第一天无法交易j次,所以这些位置也没有意义,为了不影响计算,可以初始化为无穷小。

但是对于g表的状态计算时,有减法的操作,可能会超出范围。

所以可以 将 无穷小用-0x3f3f3f3f替换,这个数为无穷小的一半。

综上 ,开始时,可以将f表和g表全部初始化为-0x3f3f3f3f,f[0][0]和g[0][0]修改,其余部分在填表时做修改。

【代码】

//股票问题3
const int N = 1e5 + 10;
const int INF = -0x3f3f3f3f;
int n;
int prices[N];
int main()
{
    cin >> n;
    for (int i = 0; i < n; i++)
        cin >> prices[i];
    //j=3,交易次数为0,1,2
    vector<vector<int>> f(n, vector<int>(3, INF));
    auto g = f;
    //初始化
    f[0][0] = -prices[0];
    g[0][0] = 0;
    for (int i = 1; i < n; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            f[i][j] = max(f[i - 1][j], g[i - 1][j] - prices[i]);
            g[i][j] = g[i - 1][j];
            if (j - 1 >= 0)
                g[i][j] = max(g[i][j], f[i - 1][j - 1] + prices[i]);
        }
    }
    //交易0次,1次,2次的最大利益
    int ans = 0;
    for (int j = 0; j < 3; j++)
        ans = max(ans, g[n - 1][j]);
    cout << ans << endl;
    return 0;
}

四,买卖股票的最佳时机4

本题思路与买卖股票3的思路完全一致,只是交易次数的变化,分析思路一摸一样。只需将数组中j的大小由3改为k+1即可。 

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


const int N = 1e5 + 10;
const int INF = -0x3f3f3f3f;
int n,k;
int prices[N];
int main()
{
    cin >> n>>k;
    for (int i = 0; i < n; i++)
        cin >> prices[i];
    //j=k+1,交易次数为0,1,2......k
    vector<vector<int>> f(n, vector<int>(k+1, INF));
    auto g = f;
    //初始化
    f[0][0] = -prices[0];
    g[0][0] = 0;
    for (int i = 1; i < n; i++)
    {
        for (int j = 0; j < k+1; j++)
        {
            f[i][j] = max(f[i - 1][j], g[i - 1][j] - prices[i]);
            g[i][j] = g[i - 1][j];
            if (j - 1 >= 0)
                g[i][j] = max(g[i][j], f[i - 1][j - 1] + prices[i]);
        }
    }
    //交易0次,1次,2次的最大利益
    int ans = 0;
    for (int j = 0; j < k+1; j++)
        ans = max(ans, g[n - 1][j]);
    cout << ans << endl;
    return 0;
}

五,买卖股票的最佳时机含冷冻期

 【问题描述】

本题包含冷冻期,在卖出股票后的一天,进入冷冻状态,无法买入股票。

【思路】

动态规划(状态机问题)

首先定义状态表示:dp[i]表示第i天结束后,获得的最大利益。

但对于第i天,存在多种状态:买入状态,卖出状态,冷冻期。

所以需要重新定义状态表示:

dp[i][0]:表示第i天之后,处于买入状态,此时的最大利益。

dp[i][1]:表示第i天之后,处于卖出状态,此时的最大利益。

dp[i][2]:表示第i天之后,处于冷冻期状态,此时的最大利益。


状态转移方程分析:

dp[i][0]:第i天结束后处于买入状态。可以从两方面转移得到:1,前一天处于买入状态,今天仍处于买入状态,即dp[i-1][0]。2,前一天处于卖出状态,今天买入股票后,处于买入状态,即dp[i-1][1]-prices[i]。

所以可得:dp[i][0]=max(dp[i-1][0],dp[i-1][1]-prices[i])。

dp[i][1]:第i天结束后处于卖出状态。可以从两方面转移得到:1,前一天处于卖出状态,今天人处于卖出状态,即dp[i-1][1]。2,前一天处于处于冷冻期,今天就处于卖出状态,即dp[i-1][2]

所以可得:dp[i][1]=max(dp[i-1][1],dp[i-1][2])。

dp[i][2]:第i天结束之后处于冷冻期,只有一种情况:前一天处于买入状态,今天将股票卖出即可。dp[i-1][0]+prices[i]。

所以dp[i][2]=dp[i-1][0]+prices[0]。


初始化问题

dp[0][0]表示第一天处于买入状态,所以dp[0][0]=-prices[0]。

dp[0][1]表示第一天处于卖出状态,所以dp[0][1]=0。

dp[0][2]表示第一天处于 冷冻期,那我们可以将第一天的股票买了又卖掉,利益还是0,dp[0][2]=0。


 返回值

只需求出最后一天处于卖出状态和冷冻期状态下的最大值即可。

【代码】

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n=prices.size();
        vector<vector<int>> dp(n,vector<int>(3));
        dp[0][0]=-prices[0];
        for(int i=1;i<n;i++)
        {
            dp[i][0]=max(dp[i-1][0],dp[i-1][1]-prices[i]);
            dp[i][1]=max(dp[i-1][2],dp[i-1][1]);
            dp[i][2]=dp[i-1][0]+prices[i];
        }
        return max(dp[n-1][1],dp[n-1][2]);
    }
};

六,买卖股票的最佳时机含手续费

 【问题描述】

在完成一次交易后(买入一次卖出一次),需要支付一次手续费,求最大利益。

支付手续费的时候,可以选择在买入的时候支付或者是在卖出的时候支付。

【思路】

 状态定义:

f[i]:表示第i天结束之后,处于买入状态,此时的最大利益。

g[i]:表示第i天结束之后,处于卖出状态,此时的最大利益。

状态转移方程:

f[i]:第i天结束之后处于买入状态。1,前一天处于买入状态,今天仍然处于买入状态,即f[i-1]。

2,前一天处于卖出状态,今天买入股票即可。即g[i-1]-prices[i]。

        所以f[i]=max(f[i-1],g[i-1]-prices[i])。

g[i]:第i天结束之后处于卖出状态。1,前一天处于卖出状态,今天仍处于卖出状态,即g[i-1]。

2,前一天处于买入状态,今天卖出股票即可,同时支付手续费。即f[i-1]+prices[i]-fee

        所以g[i]=max(g[i-1],f[i-1]+prices[i]-fee)。


 返回值 

g[n-1],表示最后一天把股票卖出去的最大利益。f[n-1]表示最后一天还持有一张股票的最大利益。

g[n-1]一定比f[i-1]大,多以返回g[n-1]即可。

【代码】

class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
        int n=prices.size();
        vector<int> f(n);
        auto g=f;
        f[0]=-prices[0],g[0]=0;
        for(int i=1;i<n;i++)
        {
            f[i]=max(f[i-1],g[i-1]-prices[i]);
            g[i]=max(g[i-1],f[i-1]+prices[i]-fee);
        }
        return g[n-1];
    }
};


网站公告

今日签到

点亮在社区的每一天
去签到