<序列DP>codevs 4748 低价购买

来源:互联网 发布:阿里云搭ss 编辑:程序博客网 时间:2024/05/22 01:29

这里是题面
–这是一道计算不重复的最长下降子序列的方案数的题
–首先很容易想到,先计算最长下降子序列,然后遍历一遍f数组,如果f[i]==LDS,ans++
–但这样一定有重复的计算
–去重1:
记g[i]为以第i个数结尾的最长下降子序列的方案数,那么g[i]一定是由点g[j]转移而来,g[j]满足:j < i ,a[j] > a[i],f[j]==f[i]-1。但是,如果在a[i]前有k个数字和a[j]相等,那么只有最后一个和它相等的数(有可能是他自己)转移过来的才是答案,其他的都是重复的计算,所以我们可以用bool数组,倒序循环,如果一个数被纪录过答案,直接跳过,那么最后的答案就是所有f[i]==LDS的g[i]的和
但是用bool数组,数组开不了这么大,codevs又显示我TLE,那就用map吧
–去重2:
但是,如果有这种情况:
a:3 2 2
g:1 1 1
这样,答案就会被记为2,显然答案应该是1
也就是说,我们忽略了a[i]本身和a[j]相等的情况,没有去重
我们可以在a数组末尾添一个极小值,保证不会有前面和他重复的数,那么最后的方案数就是g[n]
记得最长下降子序列的长度要减一
代码:

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<map>using namespace std;const int maxn=5000+10;int n,ans,maxx;int a[maxn],g[maxn],f[maxn];bool vis[maxn*1000];int main(){    scanf("%d",&n);    for(int i=1;i<=n;++i) scanf("%d",&a[i]);    n++;    a[n]=-1e9;    for(int i=1;i<=n;++i) f[i]=1;    for(int i=1;i<=n;++i)      for(int j=i-1;j>=1;--j)        if(a[j]>a[i]) f[i]=max(f[i],f[j]+1);    for(int i=1;i<=n;++i) maxx=max(maxx,f[i]);    for(int i=1;i<=n;++i)    {        if(f[i]==1) g[i]=1;        else         {            map<int,bool>vis;            for(int j=i-1;j>=1;--j)              if(a[j]>a[i]&&f[j]==f[i]-1&&!vis[a[j]])              {                vis[a[j]]=1;                g[i]+=g[j];              }        }    }    printf("%d %d",maxx-1,g[n]);    return 0;}