luogu2687逢低吸纳题解--线性DP

题目链接

https://www.luogu.org/problemnew/show/P2687

分析

题目要求你买最多天数的股票,很容易发现实际上就是求最长下降子序列,但是怎么计算方案数有点烦人

我们用$f[i]$表示以第$i$天结尾的最长下降子序列长度,$g[i]$表示以转移到以第$i$天结尾的最长下降子序列可用的方案数

那么我们可以这样进行状态转移:

1
2
3
4
5
6
7
8
9
10
for(ri i=1;i<=n;i++){
for(ri j=0;j<i;j++){
if(c[j]>c[i]&&f[i]==f[j]+1){//i可以由j转移过来
g[i]+=g[j];//加上转移到j的方案数
}
else if(c[i]==c[j]&&f[i]==f[j]){//i与j两者等价,保留i,否则会算重
g[j]=0;
}
}
}

注意最后答案不一定是g[N],因为可能有些不是以第N天结尾的最长下降子序列长度也与答案一样

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <queue>
#define ll double
#define ri register int
using std::min;
using std::max;
template <class T>inline void read(T &x){
x=0;int ne=0;char c;
while(!isdigit(c=getchar()))ne=c=='-';
x=c-48;
while(isdigit(c=getchar()))x=x*10+c-48;
x=ne?-x:x;return ;
}
const int maxn=5005;
const int inf=0x7fffffff;
int n;
ll cnt=0;
ll g[maxn];
ll c[maxn],f[maxn],ans=-inf;
int main(){
read(n);
for(ri i=1;i<=n;i++){
scanf("%lf",&c[i]);//read(c[i]);
}
memset(f,0,sizeof(f));
c[0]=inf;
for(ri i=1;i<=n;i++){
for(ri j=0;j<i;j++){
if(c[j]>c[i]){
f[i]=max(f[i],f[j]+1);
}
}
ans=max(ans,f[i]);
}
g[0]=1;
for(ri i=1;i<=n;i++){
for(ri j=0;j<i;j++){
if(c[j]>c[i]&&f[i]==f[j]+1){//i可以由j转移过来
g[i]+=g[j];//加上转移到j的方案数
}
else if(c[i]==c[j]&&f[i]==f[j]){//i与j两者等价,保留i
g[j]=0;
}
}
//if(f[i]==ans)cnt+=g[i];
//不能加在这里,因为之后g[j]可能为0
}
for(ri i=1;i<=n;i++)if(f[i]==ans)cnt+=g[i];
printf("%.0lf %.0lf\n",ans,cnt);
return 0;
}