问题描述
人们选择手机号码时都希望号码好记、吉利。比如号码中含有几位相邻的相同数字、不含谐音不吉利的数字等。手机运营商在发行新号码时也会考虑这些因素,从号段中选取含有某些特征的号码单独出售。为了便于前期规划,运营商希望开发一个工具来自动统计号段中满足特征的号码数量。
工具需要检测的号码特征有两个:号码中要出现至少3个相邻的相同数字,号码中不能同时出现8和4。号码必须同时包含两个特征才满足条件。满足条件的号码例如:13000988721、23333333333、14444101000。而不满足条件的号码例如:1015400080、10010012022。
手机号码一定是11位数,前不含前导0。工具接收两个数L和R,自动统计出[L,R]区间内所有满足条件的号码数量。L和R也是11位的手机号码。
输入格式
输入文件内容只有一行,为空格分隔的2个正整数L,R。
输出格式
输出文件内容只有一行,为1个整数,表示满足条件的手机号数量。
样例输入
12121284000 12121285550
样例输出
5
提示
样例解释:
满足条件的号码:12121285000、12121285111、12121285222、12121285333、12121285550。
数据范围:
对于100%的测试数据,10^10≤L≤R<10^11。
按照题目中的限制自然地开出以下几维:
是否含有4
是否含有8
是否含有连续的3个数(以及配套的:前一个数,前一个数的前一个数)
为了搜索,开出以下两维:
当前讨论到第几位
之前是否都取到了最大
唯一需要稍作解释的就是“之前是否都取到了最大”。如果之前没有取到最大值,那么这一位最大就可以取9;反之则最大只能取当前讨论号码对应位上的数。举个例子:
讨论12345678910,如果第一位取1,讨论到第二位,那么第二位只能取0,1,2;如果第一位取0,那么第二位可以取0~9。
接下来就很容易写搜索了。为了不TLE,这里采用记忆化搜索。注意:如果之前没有都取到最大值,那么这个状态的数值才是任何时候都适用的。
#include
#include
#define ll long long
using namespace std;
ll cur[12],L[12],R[12],A,B,f[12][2][2][2][2][12][12];
ll DFS(ll x,ll m8,ll m4,ll m3,ll ml,ll l2,ll l1)
{
/*从左往右:当前讨论到第几位,是否含有8,是否含有4,是否出现了连续的三个数,之前是否都取到了最大,前一位的前一位,前一位*/
if(m8&&m4)return 0;
if(x==12)return m3;
if(!ml&&(f[x][m8][m4][m3][ml][l2][l1]!=-1))return f[x][m8][m4][m3][ml][l2][l1];
ll i,j,ans=0,a,b,c,lim;
lim=ml?cur[x]:9;
for(i=0;i<=lim;i++)
{
a=b=c=0;
if(i==8)a=1;
if(i==4)b=1;
if(i==l2&&i==l1)c=1;
ans+=DFS(x+1,m8|a,m4|b,m3|c,ml&&(i==lim),l1,i);
}
if(!ml)f[x][m8][m4][m3][ml][l2][l1]=ans;
return ans;
}
int main()
{
ll i,X,Y;
scanf("%lld%lld",&X,&Y);
X--;
for(i=1;i<12;i++)L[12-i]=X%10,X/=10;
for(i=1;i<12;i++)R[12-i]=Y%10,Y/=10;
memset(f,-1,sizeof(f));
for(i=1;i<12;i++)cur[i]=L[i];
A=DFS(1,0,0,0,1,10,10);
//一开始不存在前一位,所以给l1,l2都赋了一个不可能的值
//注意一开始ml取1
for(i=1;i<12;i++)cur[i]=R[i];
B=DFS(1,0,0,0,1,10,10);
printf("%lld",B-A);
}