hdu_4352_XHXJ's LIS 数位DP/状态压缩

给你一个LL范围内的区间,问你在这个区间内最长递增子序列长度恰为K的数有多少个

/*
 * HDU 4352 XHXJ's LIS
 * 问L到R,各位数字组成的严格上升子序列的长度为K的个数。
 * 0<L<=R<263-1 and 1<=K<=10
 * 注意这里最长上升子序列的定义,和LIS是一样的,不要求是连续的
 * 所以用十位二进制表示0~9出现的情况,和O(nlogn)求LIS一样的方法进行更新
 * 
 */

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
using namespace std;
long long dp[25][1<<10][11];
/*
 * dp[i][j][k]:i为当前进行到的数位,j状态压缩,为10个数字出现过的,其中1的个数就是最长上升子序列,k要求的上升子序列的长度
 */
int K;
int getnews(int x,int s)//更新新的状态
{
    for(int i=x;i<10;i++)
        if(s&(1<<i))return (s^(1<<i))|(1<<x);
    return s|(1<<x);
}
int getnum(int s)//得到状态s中1的个数
{
    int ret=0;
    while(s)
    {
        if(s&1)ret++;
        s>>=1;
    }
    return ret;
}
int bit[25];
long long dfs(int pos,int s,bool e,bool z)//e是是不是上界标记,z是是不是前面的为0标记
{
    if(pos==-1)return getnum(s)==K;
    if(!e &&dp[pos][s][K]!=-1)return dp[pos][s][K];
    long long ans=0;
    int end=e?bit[pos]:9;
    for(int i=0;i<=end;i++)
        ans+=dfs(pos-1,(z&&i==0)?0:getnews(i,s),e&&i==end,z&&(i==0));
    if(!e)dp[pos][s][K]=ans;
    return ans;
}
long long calc(long long n)
{
    int len=0;
    while(n)
    {
        bit[len++]=n%10;
        n/=10;
    }
    return dfs(len-1,0,1,1);
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int T;
    long long l,r;
    memset(dp,-1,sizeof(dp));
    scanf("%d",&T);
    int iCase=0;
    while(T--)
    {
        iCase++;
        scanf("%I64d%I64d%d",&l,&r,&K);
        printf("Case #%d: ",iCase);
        printf("%I64d
",calc(r)-calc(l-1));
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Aragaki/p/7578738.html