<序列DP>codevs 4748 低价购买

这里是题面
–这是一道计算不重复的最长下降子序列的方案数的题
–首先很容易想到,先计算最长下降子序列,然后遍历一遍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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值