狀態機+閆氏DP分析法團滅股票問題

前言

第一次遇到股票問題時候無從下手,看題解寫的也是含糊不清,其實他們寫的題解隱含了很多重要信息導致股票問題變得很怪,今天將展示用狀態機+閆氏DP分析法團滅股票問題。

股票買賣 I

題目描述

給定一個長度爲 N 的數組,數組中的第 i 個數字表示一個給定股票在第 i 天的價格。

如果你最多隻允許完成一筆交易(即買入和賣出一支股票),設計一個算法來計算你所能獲取的最大利潤。

注意你不能在買入股票前賣出股票。

輸入格式
第一行包含整數 N,表示數組長度。

第二行包含 N 個不大於 109 的正整數,表示完整的數組。

輸出格式
輸出一個整數,表示最大利潤。

數據範圍
1≤N≤105,

輸入樣例1:
6
7 1 5 3 6 4
輸出樣例1:
5
輸入樣例2:
5
7 6 4 3 1
輸出樣例2:
0
樣例解釋
樣例1:在第 2 天(股票價格 = 1)的時候買入,在第 5 天(股票價格 = 6)的時候賣出,最大利潤 = 6-1 = 5 。注意利潤不能是 7-1 = 6, 因爲你不能在買入股票前賣出股票。

樣例2:在這種情況下, 不進行任何交易, 所以最大利潤爲 0。

算法描述 狀態機DP 時間複雜度O(n),空間複雜度O(n)

在這裏插入圖片描述
之後我們處理邊界就好了,代碼如下:

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int INF = 0x3f3f3f3f;

const int N = 100010;

int f[N][2][2];

int main(){

    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);

    int n;

    f[0][1][1] = -INF;
    f[0][1][0] = -INF;
    f[0][0][1] = -INF;

    scanf("%d",&n);    

    for(int i=1;i<=n;i++)
    {
        int w;
        scanf("%d",&w);
        f[i][0][0] = f[i - 1][0][0];
        f[i][0][1] = max(f[i - 1][0][1],f[i - 1][1][0] + w);
        f[i][1][0] = max(f[i - 1][1][0],f[i - 1][0][0] - w);
    }

    printf("%d\n",max(f[n][0][1],f[n][0][0]));

    return 0;
}

股票買賣 II

題目描述

給定一個長度爲 N 的數組,數組中的第 i 個數字表示一個給定股票在第 i 天的價格。

設計一個算法來計算你所能獲取的最大利潤。你可以儘可能地完成更多的交易(多次買賣一支股票)。

注意:你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。

輸入格式
第一行包含整數 N,表示數組長度。

第二行包含 N 個不大於 10000 的正整數,表示完整的數組。

輸出格式
輸出一個整數,表示最大利潤。

數據範圍
1≤N≤105
輸入樣例1:
6
7 1 5 3 6 4
輸出樣例1:
7
輸入樣例2:
5
1 2 3 4 5
輸出樣例2:
4
輸入樣例3:
5
7 6 4 3 1
輸出樣例3:
0
樣例解釋
樣例1:在第 2 天(股票價格 = 1)的時候買入,在第 3 天(股票價格 = 5)的時候賣出, 這筆交易所能獲得利潤 = 5-1 = 4 。隨後,在第 4 天(股票價格 = 3)的時候買入,在第 5 天(股票價格 = 6)的時候賣出, 這筆交易所能獲得利潤 = 6-3 = 3 。共得利潤 4+3 = 7。

樣例2:在第 1 天(股票價格 = 1)的時候買入,在第 5 天 (股票價格 = 5)的時候賣出, 這筆交易所能獲得利潤 = 5-1 = 4 。注意你不能在第 1 天和第 2 天接連購買股票,之後再將它們賣出。因爲這樣屬於同時參與了多筆交易,你必須在再次購買前出售掉之前的股票。

樣例3:在這種情況下, 不進行任何交易, 所以最大利潤爲 0。

算法描述 狀態機DP 時間複雜度O(n),空間複雜度O(n)

在上題的基礎上去掉了K的限制,問題,狀態表示與上題類似,這個問題也迎刃而解。
代碼如下:

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 100010 , INF = 0x3f3f3f3f;

int f[N][2];

int n;

int main(){

    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);

    scanf("%d",&n);

    f[0][1] = -INF;

    for(int i=1;i<=n;i++)
    {
        int w;
        scanf("%d",&w);
        f[i][0] = max(f[i - 1][1] + w,f[i - 1][0]);
        f[i][1] = max(f[i - 1][0] - w,f[i - 1][1]);
    }

    printf("%d\n",f[n][0]);

    return 0;
}

股票買賣 III

題目描述

給定一個長度爲 N 的數組,數組中的第 i 個數字表示一個給定股票在第 i 天的價格。

設計一個算法來計算你所能獲取的最大利潤。你最多可以完成兩筆交易。

注意: 你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。

輸入格式
第一行包含整數 N,表示數組長度。

第二行包含 N 個不大於 109 的正整數,表示完整的數組。

輸出格式
輸出一個整數,表示最大利潤。

數據範圍
1≤N≤105
輸入樣例1:
8
3 3 5 0 0 3 1 4
輸出樣例1:
6
輸入樣例2:
5
1 2 3 4 5
輸出樣例2:
4
輸入樣例3:
5
7 6 4 3 1
輸出樣例3:
0
樣例解釋
樣例1:在第 4 天(股票價格 = 0)的時候買入,在第 6 天(股票價格 = 3)的時候賣出,這筆交易所能獲得利潤 = 3-0 = 3 。隨後,在第 7 天(股票價格 = 1)的時候買入,在第 8 天 (股票價格 = 4)的時候賣出,這筆交易所能獲得利潤 = 4-1 = 3 。共得利潤 3+3 = 6。

樣例2:在第 1 天(股票價格 = 1)的時候買入,在第 5 天 (股票價格 = 5)的時候賣出, 這筆交易所能獲得利潤 = 5-1 = 4 。注意你不能在第 1 天和第 2 天接連購買股票,之後再將它們賣出。因爲這樣屬於同時參與了多筆交易,你必須在再次購買前出售掉之前的股票。

樣例3:在這種情況下, 不進行任何交易, 所以最大利潤爲 0。

算法分析,狀態機DP(滾動數組優化空間複雜度),時間複雜度O(n),空間複雜度O(1)
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

int f[2][2][3];

int main(){

    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);

    memset(f,-0x3f,sizeof f);

    f[0][0][0] = 0;

    int n;

    scanf("%d",&n);

    for(int i=1;i<=n;i++)
    {
        int w;
        scanf("%d",&w);
        for(int k=0;k<=2;k++)
        {
            f[i & 1][0][k] = f[i - 1 & 1][0][k];
            f[i & 1][1][k] = max(f[i - 1 & 1][1][k],f[i - 1 & 1][0][k] - w);
            if(k)
                f[i & 1][0][k] = max(f[i & 1][0][k],f[i - 1 & 1][1][k - 1] + w);
        }
    }

    int res = max(max(f[n & 1][0][0],f[n & 1][0][1]),f[n & 1][0][2]);

    printf("%d\n",res);

    return 0;
}

股票買賣 IV

題目描述

給定一個長度爲 N 的數組,數組中的第 i 個數字表示一個給定股票在第 i 天的價格。

設計一個算法來計算你所能獲取的最大利潤,你最多可以完成 k 筆交易。

注意:你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。一次買入賣出合爲一筆交易。

輸入格式
第一行包含整數 N 和 k,表示數組的長度以及你可以完成的最大交易數量。

第二行包含 N 個不超過 10000 的正整數,表示完整的數組。

輸出格式
輸出一個整數,表示最大利潤。

數據範圍
1≤N≤105,
1≤k≤100

輸入樣例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.

算法分析:幾乎和上題一樣,就是把交易次數變多了,不講了。
#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int M = 110;

int f[2][2][M];

int main(){

    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);

    memset(f,-0x3f,sizeof f);

    f[0][0][0] = 0;

    int n,m;

    scanf("%d%d",&n,&m);

    for(int i=1;i<=n;i++)
    {
        int w;
        scanf("%d",&w);
        for(int k=0;k<=m;k++)
        {
            f[i & 1][1][k] = max(f[i - 1 & 1][1][k],f[i - 1 & 1][0][k] - w);
            f[i & 1][0][k] = f[i - 1 & 1][0][k];
            if(k)
                f[i & 1][0][k] = max(f[i & 1][0][k],f[i - 1 & 1][1][k - 1] + w);
        }
    }

    int res = 0;

    for(int i=0;i<=m;i++)
        res = max(res , f[n & 1][0][i]);
    
    printf("%d\n",res);

    return 0;
}

股票買賣 V

題目描述

給定一個長度爲 N 的數組,數組中的第 i 個數字表示一個給定股票在第 i 天的價格。

設計一個算法計算出最大利潤。在滿足以下約束條件下,你可以儘可能地完成更多的交易(多次買賣一支股票):

你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。
賣出股票後,你無法在第二天買入股票 (即冷凍期爲 1 天)。
輸入格式
第一行包含整數 N,表示數組長度。

第二行包含 N 個不超過 10000 的正整數,表示完整的數組。

輸出格式
輸出一個整數,表示最大利潤。

數據範圍
1≤N≤105

輸入樣例:
5
1 2 3 0 2
輸出樣例:
3

樣例解釋
對應的交易狀態爲: [買入, 賣出, 冷凍期, 買入, 賣出],第一筆交易可得利潤 2-1 = 1,第二筆交易可得利潤 2-0 = 2,共得利潤 1+2 = 3。

算法分析:狀態機DP 時間複雜度O(n) , 空間複雜度O(1)

增加了一個凍結期,也是非常容易的,直接看代碼

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

int f[2][3];//0解凍期(不持有),1凍結期(不持有),2,持有

int main(){

    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);

    int n;

    scanf("%d",&n);
    
    memset(f,-0x3f,sizeof f);

    f[0][0] = 0;
    f[0][1] = 0;

    for(int i=1;i<=n;i++)
    {
        int w;
        scanf("%d",&w);
        f[i & 1][2] = max(f[i - 1 & 1][2],f[i - 1 & 1][0] - w);
        f[i & 1][1] = f[i - 1 & 1][2] + w;
        f[i & 1][0] = max(f[i - 1 & 1][0],f[i - 1 & 1][1]);

    }

    int res = max(f[n & 1][0],f[n & 1][1]);
    
    printf("%d\n",res);

    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章