数位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(len−1,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)