hdu 4352 XHXJ's LIS

题目大意:让你求l到r 之间数位的最长上升子序列的个数为k 的数的数量。

思路:用状态压缩模拟求最长上升子序列的过程,因为最多不超过10个所以可以压缩, 用dp[ i ][ j ][ k ]表示数位为i,最长上升子序列的状态为j,

最后最长上升序列为k的方案数。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll f[20][1<<11][11];
ll dig[20],tot,k;
ll cal(ll s,ll u,ll &len)
{
    ll i;
    for(i=u;i<10;i++)
        if(s&(1<<i)) break;
    if(i<10)
    {
        s^=(1<<i);
        s^=(1<<u);
    }
    else s^=(1<<u),len++;
    return s;
}
ll dp(ll pos,ll s,ll len,bool limit,bool lead)
{
    if(pos==-1)
        return len==k;
    if(!limit && f[pos][s][k]!=-1)
        return f[pos][s][k];
    ll up=limit? dig[pos]:9;
    ll ans=0;
    for(ll i=0;i<=up;i++)
    {
        ll nx_s,nx_len=len;
        if(lead && i==0) nx_s=0;
        else nx_s=cal(s,i,nx_len);
        ans+=dp(pos-1,nx_s,nx_len,limit && i==up,lead && i==0);
    }
    if(!limit) f[pos][s][k]=ans;
    return ans;

}
ll solve(ll x)
{
    tot=0;
    while(x)
    {
        dig[tot++]=x%10;
        x/=10;
    }
    return dp(tot-1,0,0,true,1);
}
int main()
{
    memset(f,-1,sizeof(f));
    ll T; scanf("%I64d",&T);
    for(ll cas=1;cas<=T;cas++)
    {
        ll l,r; scanf("%I64d%I64d%I64d",&l,&r,&k);
        ll ans1=solve(r);
        ll ans2=solve(l-1);
        printf("Case #%I64d: %I64d
",cas,ans1-ans2);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/CJLHY/p/8391978.html