XHXJ's LIS

题目链接:https://vjudge.net/problem/HDU-4352

题意:题目意思就是给你L到R区间,和一个数字K,然后让你求L到R区间之内满足最长上升子序列长度为K的数字有多少个;

思路:dp[i][j][k]表示前i位,用二进制表示最长上升子序列都出现了那些数,最长上升子序列的长度为K。每次对新加的数字进行判断,看如果他前面一个数字是否比他小,那就替换,没有就将其加入。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll dp[25][1<<10][25];
int a[20];
ll k;
int fun(int s)
{
    int sum=0;
    while(s)
    {
        if(s%2==1)
            sum++;
        s/=2;
    }
    return sum;
}
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);
}
//len代表当前枚举的位,s表示哪些数已经选过了的状态,flag代表是否还是处于上边界,qd表示是否为前导0
ll dfs(int len,int s,bool flag,bool qd)
{
    if(len==0)
        return fun(s)==k;
    int up=flag?a[len]:9;//决定你枚举的上界是多少
    if(!flag&&dp[len][s][k]!=-1)
        return dp[len][s][k];//如果不是处于上边界并且之前求过了值的话可以直接返回
    ll tmp=0;
    for(int i=0; i<=up; i++)
    {
        tmp+=dfs(len-1,(qd&&i==0)?0:getnews(i,s),flag&&i==a[len],qd&&(i==0));
    }
    if(!flag)
        dp[len][s][k]=tmp;//求了值用dp存下,下次可以使用
    return tmp;
}
ll suan(ll x)
{
    int pos=0;
    while(x)
    {
        a[++pos]=x%10;
        x/=10;
    }
    return dfs(pos,0,true,true);
}
int main()
{
    ll l,r;
    memset(dp,-1,sizeof(dp));
    int t,u=0;
    cin>>t;
    while(t--)
    {
        cin>>l>>r>>k;
        printf("Case #%d: ",++u);
        cout<<suan(r)-suan(l-1)<<endl;
    }
}
原文地址:https://www.cnblogs.com/zcb123456789/p/13678619.html