- 专题四 数位dp
启示录
- @ 2025-8-9 15:18:51
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL f[31][4];
int n;
void init()
{
f[0][0] = 1;
for(int i = 1; i <= 20; i++)
{
f[i][0] = (f[i - 1][0] + f[i - 1][1] + f[i - 1][2]) * 9;
f[i][1] = f[i - 1][0];
f[i][2] = f[i - 1][1];
f[i][3] = f[i - 1][3] * 10 + f[i - 1][2];
}
}
int main()
{
int t;
cin >> t;
init();
while(t --)
{
cin >> n;
int len = 3;
while(f[len][3] < n) len ++; //确定位数
LL now = 0;
for(int i = len; i >= 1; i --)
{
for(int j = 0; j <= 9; j++)
{
LL cnt = f[i-1][3];
if(now == 3 || j == 6) //现在存在多少个魔鬼数
{
int kk = max((LL)0, 3 - now - (j == 6));
for(int k = kk; k <= 2; k ++)
cnt += f[i - 1][k];
}
if(cnt < n) n -= cnt;
else {
if(now < 3) {
if(j == 6)
now ++;
else now = 0;
}
cout << j;
break;
}
}
}
cout << endl;
}
return 0;
}
/*
试填法
状态表示:f[i][j]:当前的数有i位。
j=0表示最后一位不是6
j=1表示最后一位是6,但前一位不是
j=2 表示当前的6连在一起的有两个
j=3 表示已经凑出来一个魔鬼数
状态计算: f[i][0] = (f[i - 1][0] + f[i - 1][1] + f[i - 1][2]) * 9
f[i][j] = f[i - 1][j - 1]
f[i][3] = f[i - 1][3] * 10 + f[i - 1][2];
利用试填法:
先确定位数len
对于每一位i, 从高到低,枚举当前位数p
1.cnt表示我们如果把这一位赋值为p的魔鬼数的方案数,cnt = f[i-1][3]
2.如果当前数位6的后继,或者我们已经有了一个魔鬼数后缀,那么cnt += 当前前缀的方案数
3.cnt < n , 将n减去cnt,继续枚举这一位的取值。
4.cnt >= n, 说明这一位就是p,输出p,然后更新连续6的个数。i++
*/
0 条评论
目前还没有评论...