题目地址:
https://www.acwing.com/problem/content/1059/
给定一个长度为 N N N的数组,数组中的第 i i i个数字表示一个给定股票在第 i i i天的价格。设计一个算法来计算你所能获取的最大利润,你最多可以完成 k k k笔交易。注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。一次买入卖出合为一笔交易。
输入格式:
第一行包含整数 N N N和 k k k,表示数组的长度以及你可以完成的最大交易数量。第二行包含 N N N个不超过 10000 10000 10000的正整数,表示完整的数组。
输出格式:
输出一个整数,表示最大利润。
数据范围:
1 ≤ N ≤ 105 1≤N≤105 1≤N≤105
1 ≤ k ≤ 100 1≤k≤100 1≤k≤100
思路是动态规划。可以将整个买卖过程视为一个状态机接受一个输入,状态机有两个状态,分别是有股和无股,并且设 f [ i ] [ j ] [ 0 ] f[i][j][0] f[i][j][0]是到第 i i i天为止,一共恰好进行过 j j j次买入,并且第 i i i天结束时无股的情况下的最大利润,设 f [ i ] [ j ] [ 1 ] f[i][j][1] f[i][j][1]是到第 i i i天为止,一共进行过 j j j次买入,并且第 i i i天结束时有股的情况下的最大利润。设 p [ i ] p[i] p[i]是第 i i i天的股价,那么就有:
1、今天的无股状态,可以由昨天无股或者昨天有股转移过来,如果昨天无股,那么最大利润是 f [ i − 1 ] [ j ] [ 0 ] f[i-1][j][0] f[i−1][j][0];如果昨天有股,则是 f [ i − 1 ] [ j ] [ 1 ] + p [ i ] f[i-1][j][1]+p[i] f[i−1][j][1]+p[i];
2、今天的有股状态,也可以由昨天无股或者昨天有股转移过来,如果昨天无股,那么最大利润是 f [ i − 1 ] [ j − 1 ] [ 0 ] − p [ i ] f[i-1][j-1][0]-p[i] f[i−1][j−1][0]−p[i];如果昨天有股,则是 f [ i − 1 ] [ j ] [ 1 ] f[i-1][j][1] f[i−1][j][1];
所以有: { 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 ] [ 0 ] − p [ i ] , f [ i − 1 ] [ j ] [ 1 ] } \begin{cases}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][0]-p[i],f[i-1][j][1]\} \end{cases} {
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][0]−p[i],f[i−1][j][1]}考虑初始条件,一开始第 0 0 0天,只有进行 0 0 0次买入并且无股的状态是合法的,利润是 0 0 0,所以 f [ 0 ] [ 0 ] [ 0 ] = 0 f[0][0][0]=0 f[0][0][0]=0,其余情况都不合法,其转移出来的状态都不能作为答案,可以令 f [ 0 ] [ 0 ] [ 1 ] = f [ 0 ] [ . > 0 ] [ 0 ] = f [ 0 ] [ . > 0 ] [ 1 ] = − ∞ f[0][0][1]=f[0][.>0][0]=f[0][.>0][1]=-\infty f[0][0][1]=f[0][.>0][0]=f[0][.>0][1]=−∞。代码如下:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 100010, K = 110;
int n, k;
int a[N], f[N][K][2];
int main() {
cin >> n >> k;
for (int i = 1; i <= n; i++) cin >> a[i];
int res = 0;
// 这里的优化的意思是,如果k >= n / 2,那么等价于可以进行无限次交易
if (k >= n / 2) {
for (int i = 2; i <= n; i++)
res += max(0, a[i] - a[i - 1]);
cout << res << endl;
return 0;
}
memset(f, -0x3f, sizeof f);
f[0][0][0] = 0;
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= k; j++) {
f[i][j][0] = max(f[i - 1][j][0], f[i - 1][j][1] + a[i]);
f[i][j][1] = f[i - 1][j][1];
if (j >= 1) f[i][j][1] = max(f[i][j][1], f[i - 1][j - 1][0] - a[i]);
}
}
cout << f[n][k][0] << endl;
return 0;
}
时空复杂度 O ( N k ) O(Nk) O(Nk)。