题解 P2687 【[USACO4.3]逢低吸纳Buy Low, Buy Lower 】
题目大意
求最长降序列,并求出不同的方案数
解法
第一问
求最长降序列
因为n只有5000,所以O(n²)的DP即可解决
f[i]表示以i为结尾,其中第i个必取最长上降序列的长度
从之前的数中找上一状态即可
for(int j=1;j<i;j++) if(a[i]<a[j]) f[i]=max(f[i],f[j]+1);
第二问
求方案数
与前一问的DP类似
这里num[i]表示以第i个为结尾,长度为f[i]的方案数
从前面找上一状态累计上即可
if(a[i]<a[j]&&f[i]==f[j]+1) num[i]+=num[j];
但题目要求方案各不相同
特判一模一样的情况,并且减去即可
if(a[i]==a[j]&&f[i]==f[j]) num[i]-=num[j];
细节
因为答案可能会很大,需要高精
但此题数据用double也行
代码
#include<bits/stdc++.h>
using namespace std;
int a[5005],n,f[5005],ans=1;
double num[5005];//类型换成double,勉强可以通过
int read(){
int ret=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-f;ch=getchar();}
while(ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
return ret*f;
}
int main(){
// freopen("buylow.in","r",stdin);
// freopen("buylow.out","w",stdout);
n=read();
for(int i=1;i<=n;i++) a[i]=read(),f[i]=1;
for(int i=2;i<=n;i++)
for(int j=1;j<i;j++) if(a[i]<a[j]) f[i]=max(f[i],f[j]+1);//第一问
for(int i=1;i<=n;i++) if(f[i]>1) ans=max(ans,f[i]);else num[i]=1;//如果是序列开头,方案就是1
f[n+1]=ans+1;//方便起见,把答案累计在第n+1项上
for(int i=2;i<=n+1;i++)
for(int j=1;j<i;j++){
if(a[i]<a[j]&&f[i]==f[j]+1) num[i]+=num[j];//刚好是上一状态
if(a[i]==a[j]&&f[i]==f[j]) num[i]-=num[j];//一样的序列
}
printf("%lld %.0lf\n",ans,num[n+1]);
return 0;
}