版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
手机号码
题目描述
人们选择手机号码时都希望号码好记、吉利。比如号码中含有几位相邻的相同数字、不含谐音不吉利的数字等。手机运营商在发行新号码时也会考虑这些因素,从号段中选取含有某些特征的号码单独出售。为了便于前期规划,运营商希望开发一个工具来自动统计号段中满足特征的号码数量。
工具需要检测的号码特征有两个:号码中要出现至少3个相邻的相同数字;号码中不能同时出现4和8 。号码必须同时包含两个特征才满足条件。满足条件的号码例如:13000988721、23333333333、14444101000。而不满足条件的号码例如:1015400080、10010012022。手机号码一定是11位数,且不含前导0 。工具接收两个数 l 和 r , 自动统计出区间内所有满足条件的号码数量。l 和 r 也是11位的手机号码。
·······························
输入格式
输入文件内容只有一行,为空格分隔的两个正整数 L, R
输出格式
输出文件内容只有一行,为一个整数,表示满足条件的手机号数量。
分析:
很显然, 使用数位dp, 详见注释
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAXM 15
#define LL long long
#define Int register int
using namespace std;
LL num[MAXM];
LL dp[MAXM][MAXM][MAXM][2][2][2][2];
inline LL Max(LL x,LL y)
{
return x > y ? x : y;
}
inline LL Min(LL x,LL y)
{
return x < y ? x : y;
}
inline void read(LL &x)
{
x = 0;
LL f = 1;
char s = getchar();
while (s < '0' || s > '9')
{
if (s == '-')
f = -1;
s = getchar();
}
while (s >= '0' && s <= '9')
{
x = (x << 3) + (x << 1) + (s ^ 48);
s = getchar();
}
x *= f;
}
LL Num_dp(int p,int a,int b,bool l,bool s,bool c4,bool c8)
{
/*
p : 当前数位
a : p + 1位的数
b : p + 2位的数, 如果为前导0则为 -1
l : 判断连续
s : 判断是否前面已经严格小于, 方便填数
c4、c8 : 判断 4 和 8
*/
if (c4 && c8)
return 0;
if (p <= 0)
return l;
if (~ dp[p][a][b][l][s][c4][c8])
return dp[p][a][b][l][s][c4][c8];
LL Sum = 0, Limit = ! s ? num[p] : 9;
for (Int i = 0; i <= Limit; ++ i)
Sum += Num_dp(p - 1, i, a, l || (i == a && a == b), s || (i < Limit), c4 || (i == 4), c8 || (i == 8));
return dp[p][a][b][l][s][c4][c8] = Sum;
}
LL solve(LL x)
{
if (x < 10000000000ll)
return 0;
memset(dp, -1, sizeof dp);
int len = 0;
while ( x )
{
num[++ len] = x % 10;
x /= 10;
}
LL Ans = 0;
for (Int i = 1; i <= num[len]; ++ i)
Ans += Num_dp(10, i, 0, 0, i < num[len], i == 4, i == 8);
return Ans;
}
int main()
{
LL L, R;
read( L ); read( R );
printf("%lld", solve( R ) - solve(L - 1));
return 0;
}