数位DPXHXJ‘s LIS HDU - 4352

Posted Vincent_0000

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数位DPXHXJ‘s LIS HDU - 4352相关的知识,希望对你有一定的参考价值。

题目来源

点我进入提交题目

反思

这个题目差一点点就做出来了,让整个LIS数组变成一个性质储存在集合中没有想出对应的方法,现在算是学到了,将这个数组状态压缩成二进制数即可。
现在做了几道数位DP的题目了,感觉递归的写法要比递推的写法好很多,暂时没有发现递推的写法的优势(如果有大佬知道这个的优势,可以在评论区留言告诉我嘛?)

题目思路

  • 题目分析

题目求区间中的数字的最长上升子序列长度为k的个数。
有数字 有区间 有最长上升子列, 初步判断考点为LIS + 数位DP。

  • DP分析

集合定义为三维: f ( l e n , l i s , k ) f(len, lis, k) f(len,lis,k)
集合代表:长度还剩下len个,前面所取的数字的最长上升子序列数组状态压缩为lis,最长上升子序列长度为k。
这样定义集合就能把题目所有的情况都包含了,我的集合定义一定要根据题目来,不要自己空想。

状态转移方程:
f ( l e n , l i s , k ) + = f ( l e n − 1 , z e r o & & ! j ? 0 : S e t ( l i s , j ) , k ) f(len, lis, k) += f(len - 1, zero \\&\\& !j ? 0 : Set(lis, j), k) f(len,lis,k)+=f(len1,zero&&!j?0:Set(lis,j),k)
这里添加了一个判断前导零的细节,因为如果我们存在前导零的话,类似于07这样的数就LIS会被计算成2,但是他实际上为1,所以我们要将这种情况排除掉,将前导零的情况特判一下。

那么接下来就最后一个问题了如何将LIS数组压缩为二进制数。
LIS 原理不懂的话,你现阶段不适合做这种题目。
举个例子你们就懂了,假如我们要判断的数为1423。
定义len = 0, q[]。
下标遍历到1: len = 1, q[1] = 1;
下标遍历到2: len = 2, q[1] = 1, q[2] = 4;
下标遍历到3: len = 2, q[1] = 1, q[2] = 2;
下标遍历到4: len = 3, q[1] = 1, q[2] = 2, q[3] = 4;

那么将过程中的LIS数组状态压缩就会变为:
下标遍历到1: 00010
下标遍历到2: 10010
下标遍历到3: 00110
下标遍历到4: 10110
1的个数即为长度,将出现过的数化为1放到该数字的下标位置。

但是每次将数组压缩成数字太麻烦了,我们再仔细分析一下,看能不能将数组这个过程省略掉。
我们观察下标2到下标3的过程,就是将4位置的1消掉,增加一个2位置的1,这个过程就类似于LIS二分查找的一个过程。

重点说完了,剩下的就是套数位DP模板+修改。

AC代码

#include<bits/stdc++.h>
using namespace std;

#define _for(i, a, b) for (int i = (a); i <= (b); ++i)
#define _rep(i, a, b) for (int i = (a); i >= (b); --i)
#define debug(a) cout << #a << " = " << a << endl
#define mod(x) (x) % MOD
#define ENDL "\\n"
#define x first
#define y second
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;

const int N = 20, M = 1 << 10, MOD = 2520, INF = 0x3f3f3f3f;
ll f[N][M][N];
int num[N], k;

int Set(int x, int j){
    _for(i, j, 9) if (x & (1 << i)) return (x ^ (1 << i)) | (1 << j);
    return x | (1 << j);
}

int Get(int x){
    int cnt = 0;
    for(; x; x>>=1 ) if (x&1) cnt++;
    return cnt;
}

ll dfs(int len, int lis, bool zero, bool limit){
    if(len < 0) return Get(lis) == k;
    ll& v = f[len][lis][k];
    if (!limit && ~v) return v;

    int x = limit ? num[len] : 9;
    ll ans = 0;
    _for(j, 0, x) ans += dfs(len - 1, zero && !j ? 0 : Set(lis, j), zero && !j, limit && j == x);

    if (!limit) v = ans;
    return ans;
}

ll dp(ll n){
    int sz = 0;
    while (n) num[sz++] = n % 10,n /=  10;
    return dfs(sz - 1,  0, true, true);
}

int main()
{
#ifdef LOCAL
    freopen("data.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cout.tie(0), cin.tie(0);

    int T;
    cin >> T;
    memset(f, -1, sizeof f);
    _for(kase, 1, T){
        ll l, r;
        cin >> l >> r >> k;

        printf("Case #%d: %lld\\n", kase, dp(r) - dp(l - 1));
    }
    return 0;
}

以上是关于数位DPXHXJ‘s LIS HDU - 4352的主要内容,如果未能解决你的问题,请参考以下文章

HDU 4352 XHXJ's LIS (数位DP+状态压缩+LIS)

HDU.4352.XHXJ's LIS(数位DP 状压 LIS)

hdu4352 XHXJ's LIS(数位dp)

HDU - 4352 - XHXJ's LIS(数位DP)

hdu 4352 XHXJ's LIS 数位DP+最长上升子序列

HDU 4352 XHXJ&#39;s LIS(数位dp&amp;状态压缩)