股票买卖V
题目描述
核心思路
最大利润f是天数i的函数,每天有两个状态,手中有票和手中无票。对于无票,我们又可细分为无票的第1天(刚卖出股票)、无票的第2天(冷冻期)、$\cdots 、无票的第 、无票的第 、无票的第x$天。
状态表示:
- f [ i ] [ 1 ] f[i][1] f[i][1]表示第i天手中有票,所能获取的最大利润。
- f [ i ] [ 0 ] f[i][0] f[i][0]表示第i天是手中无票的第1天(即刚卖出股票的第1天),所能获取的最大利润。
- f [ i ] [ 2 ] f[i][2] f[i][2]表示第i天是手中无票的第2天及以后,所能获取的最大利润。
带权的有向图,点表示状态,边权表示转移的价值增量。
入口只有一个,是 f [ i ] [ 2 ] f[i][2] f[i][2],因为刚开始我肯定是没有票的,因此入口不可能是 f [ i ] [ 1 ] f[i][1] f[i][1],那么就只能从无票中进入了,但是从状态图中可以看出, f [ i ] [ 0 ] f[i][0] f[i][0]只能从 f [ i ] [ 1 ] f[i][1] f[i][1]转移过来,由于我们知道 f [ i ] [ 1 ] f[i][1] f[i][1]不可能是入口了, 那么就不可能转移到 f [ i ] [ 0 ] f[i][0] f[i][0],因此,入口就只能是 f [ i ] [ 2 ] f[i][2] f[i][2]了;但是出口有两个,但必须是无票状态,即 f [ n ] [ 0 ] f[n][0] f[n][0]和 f [ n ] [ 2 ] f[n][2] f[n][2]。
状态计算:
- f [ i ] [ 1 ] = m a x ( f [ i − 1 ] [ 1 ] , f [ i − 1 ] [ 2 ] − w [ i ] ) f[i][1]=max(f[i-1][1],f[i-1][2]-w[i]) f[i][1]=max(f[i−1][1],f[i−1][2]−w[i])
- f [ i ] [ 0 ] = f [ i − 1 ] [ 1 ] + w [ i ] f[i][0]=f[i-1][1]+w[i] f[i][0]=f[i−1][1]+w[i]
- f [ i ] [ 2 ] = m a x ( f [ i − 1 ] [ 0 ] , f [ i − 1 ] [ 2 ] ) f[i][2]=max(f[i-1][0],f[i-1][2]) f[i][2]=max(f[i−1][0],f[i−1][2])
处理边界初值:
我们的有效天数是从第1天到第n天,边界初值一般是不包含在有效天数中的。因此,我们需要对第0天做边界初值。 f [ 0 ] [ 1 ] f[0][1] f[0][1]表示第0天手中有票,所能获取的最大利润。但是由实际含义可知,第0天不可能有票,是不合法的,那么我们给它设置为负无穷大。 f [ 0 ] [ 0 ] f[0][0] f[0][0]表示第0天是手中无票的第1天,也就是刚卖出股票的第1天,由实际含义可知,第0天不可能会有票,更不可能说能卖出股票了,因此是不合法的,那么我们给它设置为负无穷大。 f [ 0 ] [ 2 ] f[0][2] f[0][2]表示第0天是手中无票的第2天及以后,那么手中无票,肯定没有收益,所以设置为0。
由于第i天都是从前i-1天转移过来的,现在考虑能不能把状态0和状态2合并看作一个新的状态0呢?即此时只有状态1和状态0:
如下图所示,
分析对于状态1有两种情况:
- 从昨天第i-1天转移到今天第i天,即我今天并没有卖掉股票,所以我手中仍然有票
- 第i-2天表示前天,第i-1天表示昨天,第i天表示今天。如果我们是从第i-1天转移过来,那么由于是状态0,所以表示第i-1天即昨天是卖出股票的第一天,那么要经过一天的冷冻期,我才能买入股票,也就是说我昨天卖出了股票,今天就不能买入股票了,因此第i-1天卖出股票,那么第i天就是冷冻期,不能买入股票。但是如果我们是从第i-2天转移过来,则表示前天卖出了股票,经过一天的冷冻期后,也就是昨天是冷冻期,那么今天就可以买入股票了,因此,是从i-2转移过来的哦。
对昨天0也是有两种情况,类似分析即可。
状态计算:
- f [ i ] [ 0 ] = m a x ( f [ i − 1 ] [ 0 ] , f [ i − 1 ] [ 1 ] + w [ i ] ) f[i][0]=max(f[i-1][0],f[i-1][1]+w[i]) f[i][0]=max(f[i−1][0],f[i−1][1]+w[i])
- f [ i ] [ 1 ] = m a x ( f [ i − 1 ] [ 1 ] , f [ i − 2 ] [ 0 ] − w [ i ] ) f[i][1]=max(f[i-1][1],f[i-2][0]-w[i]) f[i][1]=max(f[i−1][1],f[i−2][0]−w[i])
边界初值:
f [ 0 ] [ 0 ] = 0 , f [ 0 ] [ 1 ] = − I N F f[0][0]=0,f[0][1]=-INF f[0][0]=0,f[0][1]=−INF
代码
写法1
#include<iostream>
#include<algorithm>
using namespace std;
const int N=100010,INF=0x3f3f3f3f;
//f[i][1]表示第i天手中有票,所能获取的最大利润。
//f[i][0]表示第i天是手中无票的第1天(即刚卖出股票的第1天),所能获取的最大利润。
//f[i][2]表示第i天是手中无票的第2天及以后,所能获取的最大利润。
int f[N][3];
int w[N]; //w[i]表示第i天的股票价格
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&w[i]);
//处理边界
f[0][1]=f[0][0]=-INF,f[0][2]=0;
for(int i=1;i<=n;i++)
{
f[i][1]=max(f[i-1][1],f[i-1][2]-w[i]);
f[i][0]=f[i-1][1]+w[i];
f[i][2]=max(f[i-1][0],f[i-1][2]);
}
printf("%d\n",max(f[n][0],f[n][2]));
return 0;
}
写法2
#include<iostream>
#include<algorithm>
using namespace std;
const int N=100010,INF=0x3f3f3f3f;
int f[N][2];
int w[N];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&w[i]);
//边界
f[0][0]=0,f[0][1]=-INF;
for(int i=1;i<=n;i++)
{
f[i][0]=max(f[i-1][0],f[i-1][1]+w[i]);
f[i][1]=max(f[i-1][1],f[i-2][0]-w[i]);
}
printf("%d\n",f[n][0]);
return 0;
}