題名:
長さNの配列が与えられた場合、配列のi番目の数値は、i日目の特定の株式の価格を表します。
得ることができる最大の利益を計算するためのアルゴリズムを設計します。最大でk個のトランザクションを完了することができます。
注:同時に複数の取引に参加することはできません(再度購入する前に、以前の株式を売却する必要があります)。1つの購入と販売が1つのトランザクションに結合されます。
回答:
dp [i] [j] [0]が前のi日からの選択、合計jトランザクションを表すとします。現在の状態は、株式を保有していない場合の最大リターンです
。dp[i] [j] [1]は選択を表します。前のi日から合計j件の取引があり、現在のステータスは保有株式の最大リターンです
。3番目の次元は現在のステータスを示します
。dp[i] [j] [0]の場合、次の場所に株式はありません。存在する場合は、前日の在庫がないことから継承できます。前日に販売した在庫から継承することもできます
。dp[i] [j] [0]の場合、現在在庫があることを意味します。前日の在庫から継承されている場合もあれば、前日の在庫がない場合もあります。
上記の状況で最大のシチュエーションを購入したばかりです。
最終日、トランザクション数が最大で、最終的には在庫が結果です。
スペースを圧縮するために、ローリングアレイを使用して最初の次元を最適化します(実際には、01ナップザック問題の圧縮)
コード:
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1e5 + 10, M = 110;
int n, m;
int w[N];
int f[N][M][2]; //f[i][j][0]代表从前i天中选,共进行了j次交易,当前状态为不持有股票的最大收益
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);
memset(f, -0x3f, sizeof f); //要求最大值,先初始化为负无穷
for (int i = 0; i <= n; i++) f[i][0][0] = 0; //不管几天,只要没有交易收益就是0
for (int i = 1; i <= n; i++) {
//状态机见上图
for (int j = 1; j <= m; j++) {
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;
//最后一天,共交易k次,且最后不持有股票的最大值即为结果
for (int k = 0; k <= m; k ++ ) res = max(res, f[n][k][0]);
printf("%d\n", res);
return 0;
}
#include<iostream>
#include<cstdio>
#include<cstring>
typedef long long ll;
#define MAXK 111
ll f[2][MAXK];
int main()
{
ll n,k,x;
scanf("%lld%lld",&n,&k);
memset(f,0xcf,sizeof f);
f[0][0]=0;
for(ll i=1;i<=n;++i)
{
scanf("%lld",&x);
for(ll j=k;j;--j)
{
f[0][j]=std::max(f[0][j],f[1][j]+x);
f[1][j]=std::max(f[1][j],f[0][j-1]-x);
}
}
ll ans=0;
for(ll i=0;i<=k;++i)ans=std::max(ans,std::max(f[0][i],f[1][i]));
printf("%lld",ans);
return 0;
}