


题意:
定义 不吉利的数字 为所有 含有 4 或 62 的号码。
例如:62315,73418,88914 都属于 不吉利号码。
但是,61152 虽然含有 6 和 2,但不是连号,所以属于 吉利数字 之列。
给定区间 [a, b],问其中有多少个 吉利数字?
思路:
本题和之前几道数位dp的题目思考方式类似,
假设 数字 num 当前枚举到第 i 位,且 第 i 位数字是 x,那么对于 答案的第 i 位数字 j 来说,有 两种填法:
-
1.
0 ~ x - 1
用last表示num中第i位的 前面一位数字,
根据题意,j不能是4且 当last是6的时候j不能为2,两种情况在 枚举的时候特判 即可
之后,累加答案res中,res += f[i+1][j] -
2.
x
这一位则不用处理,更新last即可:last = x
f[i][j] 状态表示:(核心)
- 集合:共有
i位,且 最高位是j的 满足吉利数定义 的数的集合 - 属性:数量
f[i][j] 状态计算:(核心)
第 i 位是 j 已固定,那么 考虑第 i - 1 位,假设为 k,根据 吉利数 定义,j 为 6 时 k 不为 2 且 k 不能等于 4
状态转移方程 见下:
代码及注释:
#include<bits/stdc++.h>
using namespace std;
const int N = 15;
int f[N][N];
typedef vector<int> vi;
#define pb push_back
#define pp pop_back
void init()
{
//先预处理 1 位数的情况
for(int i=0; i<=9; ++i) f[1][i] = 1;
f[1][4] = 0;
//接下来预处理两位数及以上的情况
for(int i=2; i<N; ++i)
{
for(int j=0; j<=9; ++j)
{
for(int k=0; k<=9; ++k)
{
if(j==4 || k==4 || (j==6 && k==2)) continue;
f[i][j] += f[i-1][k];
}
}
}
}
int dp(int n)
{
if(!n) return 1;//n==0的时候显然不包含 62 or 4,算作 1 个合法方案
vi num; while(n) num.pb(n%10), n /= 10;
int res = 0;//存总方案数
int last = 0;//存当前位之前的数位信息 由于只有上一位数字是 6 且当前位数字为 2 的时候才需要特殊处理,因此 last 只需存储当前位的上一位是什么即可,last 初始化不为 6 即可,这里我们初始化为 0
for(int i=num.size()-1; i>=0; --i)
{
int x = num[i];
for(int j=0; j<x; ++j)//枚举左分支
{
if(j==4 || (j==2&&last==6)) continue;
res += f[i+1][j];
}
if(x==4 || (x==2 && last==6) ) break;//说明在右分支中,相邻两个数存在 62 或 4,即该分支不合法,直接 break
last = x;
if(!i) ++res;//如果走到了右分支末端且没有被 break,说明原数本身即为一个合法方案,将其加入答案 res 即可
}
return res;
}
int main()
{
init();
int l, r;
while(cin>>l>>r, l, r)
{
cout<<dp(r) - dp(l-1)<<'\n';
}
return 0;
}

博客内容讲述了如何通过动态规划方法解决在给定区间内计算不包含特定数字组合(4或62)的数字个数的问题。程序首先预处理不同位数的吉利数字个数,并通过动态规划递归计算区间内的不吉利数字数量。
&spm=1001.2101.3001.5002&articleId=124194181&d=1&t=3&u=6ada0dba713b468d80fd48b68ebdf821)
433

被折叠的 条评论
为什么被折叠?



