前言
第一次遇到股票問題時候無從下手,看題解寫的也是含糊不清,其實他們寫的題解隱含了很多重要信息導致股票問題變得很怪,今天將展示用狀態機+閆氏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;
}