POJ 1952 Buy Low,Buy Lower


题目大意:
求最长下降子序列及其个数(严格的…..)


分析:
求最长上升子序列很简单大家都会,那么方案数呢??记得之前做过一道题求的是最短路的计数,那道题是在更新最短路径时更新其对应方案数,这道题是不是也可以这样做呢?所以我们定义num[i]代表前i个数中最长子序列的个数,如果f[i]=f[j]+1(i>j)就代表i可以由j转移而来,所以j的方案数目包含在i中,那么num[i]=num[i]+num[j]。有了怎么转移,我们还要考虑一个严肃的问题,怎么去重??
看下面一段代码:

for(int i=2;i<=n;i++)
        for(int j=i-1;j>=1;j--){
            if(f[i]==f[j]+1&&a[j]>a[i])
                num[i]+=num[j];
            if(a[i]==a[j]){
                if(f[i]==1) 
                    num[i]=0;
                break;
            }
        }

这是什么意思呢??O(≧口≦)O
我们还是规定i>j,如果a[i]=a[j](因为我们是倒着往前找的)就代表我们不需要再往前枚举更新方案数目了,因为j之前的方案数目已经包含在j中了,而如果f[i]=1这代表什么呢??代表i前面没有比它大的数,那么显然f[j]也应该等于1,就是说ij方案数完全相同,这么来说如果我们保留i的方案数目,那么就会和j重复,而又因为i在j的后面所以我们显然是要保留j而把i的方案数目清零


代码如下:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn=5000+5;
int n,a[maxn],f[maxn],num[maxn];
inline int read(){
    char ch=getchar();
    int f=1,x=0;
    while(!(ch>='0'&&ch<='9')){
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
        x=x*10+ch-'0',ch=getchar();
    return f*x;
}
signed main(void){
    n=read();
    for(int i=1;i<=n;i++)
        a[i]=read(),f[i]=1,num[i]=0;
    for(int i=1;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)
            num[i]=1;
    for(int i=2;i<=n;i++)
        for(int j=i-1;j>=1;j--){
            if(f[i]==f[j]+1&&a[j]>a[i])
                num[i]+=num[j];
            if(a[i]==a[j]){
                if(f[i]==1) 
                    num[i]=0;
                break;
            }
        }
    int mx=0,ans=0;
    for(int i=1;i<=n;i++)
        mx=max(mx,f[i]);
    for(int i=1;i<=n;i++)
        if(f[i]==mx)
            ans+=num[i];
    cout<<mx<<" "<<ans<<endl;
    return 0;
}

by >o< neighthorn

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值