HDU 4352 数位DP + LIS

89 篇文章 1 订阅

数位DP + LIS

题意:

​ 每一个数字都有最长上升子序列,给出一个范围,求出里面的最长上升子序列为k 的数字个数。

思路:

​ 数位DP枚举的是每一个数位上的所有情况,当枚举到一个数字的时候其最长上升子序列可以压缩在一个二进制中,而二进制1出现的次数就是最长上升子序列的长度,其思想和LIS 的思想抑制。

定义: dp[i][s][k] 表示从最高位到第i位数字组成的数字最长上升子序列的长度为k的个数。

而在 dfs(pos,s,z,e) 的时候pos表示数字的第几位,s表示组成的序列状态(不断更新),z表示上一位是否为0(关乎到s的更新,如果之前都是0的话s = 0),e表示是否为边界(判断是否可以记忆化搜索)。


#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

typedef long long LL;

const int maxn = 20;

LL n,m,k;
int bit[maxn];
LL dp[maxn][1<<10][maxn];

int Getnum(int x)
{
    int ans = 0;
    while(x) {
        if(x&1) ans++;
        x >>= 1;
    }
    return ans;
}

int GetNew(int s,int x)
{
    for(int i = x;i < 10; i++)
        if(s&(1<<i)) return (s^(1<<i) | (1<<x));
    return s|(1<<x);
}

LL dfs(int pos,int s,int z,int e)
{
    if(pos < 0) return Getnum(s) == k;
    if(!e && dp[pos][s][k] != -1) return dp[pos][s][k];
    int End = e ? bit[pos] : 9;
    LL ans = 0;
    for(int i = 0;i <= End; i++) {
        ans += dfs(pos-1,(z&&i==0)?0:GetNew(s,i),z&&i==0,e&&i==End);
    }
    if(!e) dp[pos][s][k] = ans;
    return ans;
}

LL solve(LL x)
{
    int pos = 0;
    while(x) {
        bit[pos++] = x%10;
        x /= 10;
    }
    return dfs(pos-1,0,1,1);
}

int main(int argc, char const *argv[])
{
    // freopen("in.txt","r",stdin);

    memset(dp,-1,sizeof(dp));
    int ncase = 1,tt;
    scanf("%d",&tt);
    while(tt--) {
        scanf("%I64d%I64d%I64d",&n,&m,&k);
        printf("Case #%d: %I64d\n",ncase++,solve(m) - solve(n-1));
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值