Description
人们选择手机号码时都希望号码好记、吉利。比如号码中含有几位相邻的相同数字、不含谐音不
吉利的数字等。手机运营商在发行新号码时也会考虑这些因素,从号段中选取含有某些特征的号
码单独出售。为了便于前期规划,运营商希望开发一个工具来自动统计号段中满足特征的号码数
量。
工具需要检测的号码特征有两个:号码中要出现至少3个相邻的相同数字,号码中不能同
时出现8和4。号码必须同时包含两个特征才满足条件。满足条件的号码例如:13000988721、
23333333333、14444101000。而不满足条件的号码例如:1015400080、10010012022。
手机号码一定是11位数,前不含前导的0。工具接收两个数L和R,自动统计出[L,R]区间
内所有满足条件的号码数量。L和R也是11位的手机号码。
Input
输入文件内容只有一行,为空格分隔的2个正整数L,R。
10^10 < = L < = R < 10^11
Output
输出文件内容只有一行,为1个整数,表示满足条件的手机号数量。
Sample Input
12121284000 12121285550
Sample Output
5
样例解释
满足条件的号码: 12121285000、 12121285111、 12121285222、 12121285333、 12121285550
分析:
数位dp
还是这个问题:
mmm做的题目和我莫名相似,senior guard
先想状态:
无非是:
f[i][j][0/1][0/1] 第i位填的是j,到第i位有没有4,有没有8
这个是我第一次想出来的状态
但是我们不知道有没有三个相邻的相同数字,那简单,再加一维
f[i][j][k][0/1][0/1] 包括第i位,之前的k个数字都一样
但是这样只记录了最后的连续情况,这个串的连续情况我们还是不知道
那简单,我们再来一维
f[i][j][k][0/1][0/1][0/1]
第i位填的是j;包括第i位,之前的k个数字都一样;到第i位为止是否有三个连续相同;到第i位有没有4,有没有8
好像这样就很完美了
f[i+1][j][k][0/1][0/1]
等我写完了之后,发现我这种转移方法只能解决99999999999以内的
看来我们还需要加一维,表示现在的数字和边界是否吻合
所以状态就变成了这样:
f[i][j][k][0/1][0/1][0/1][0/1]
第i位填的是j;包括第i位,之前的k个数字都一样;到第i位为止是否有三个连续相同;到第i位有没有4,有没有8;是否卡上界
这样就可以转移了
tip
看网上有再加一维表示是否卡下界
我是分成了两部分,仔细一想复杂度是一样的
但是代码复杂度大幅减少
这里写代码片
#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long
using namespace std;
int f[15][15][15][2][2][2][2];
char s1[20],s2[20];
int a[20],b[20];
//f[i][j][k][0/1][0/1][0/1][0/1]
//第i位填的是j;包括第i位,之前的k个数字都一样;
//到第i位为止是否有三个连续相同;到第i位有没有4,有没有8;是否卡上界
ll doit(int *a)
{
int i,j,k,l,s,p,o,c,d;
bool f1,f2;
memset(f,0,sizeof(f));
for (i=1;i<=a[1];i++) f[1][i][1][0][i==4 ? 1:0][i==8 ? 1:0][i==a[1] ? 1:0]=1;
for (i=1;i<=10;i++) //2-10
for (j=0;j<=9;j++)
for (k=1;k<=i;k++)
for (l=0;l<=1;l++)
for (s=0;s<=1;s++)
for (p=0;p<=1;p++)
for (o=0;o<=1;o++)
if (f[i][j][k][l][s][p][o])
{
int tt;
if (o) tt=a[i+1]; //卡上界
else tt=9;
for (c=0;c<=tt;c++)
{
int x=1;
if (c==j) x=k+1;
f[i+1][c][x][l||x>=3][s||c==4][p||c==8][o&c==a[i+1]]+=f[i][j][k][l][s][p][o];
}
}
ll ans=0;
for (i=0;i<=9;i++)
for (j=1;j<=11;j++)
for (k=0;k<=1;k++)
ans+=(ll)f[11][i][j][1][0][1][k]+f[11][i][j][1][1][0][k]+f[11][i][j][1][0][0][k];
return ans;
}
int main()
{
scanf("%s%s",&s1,&s2);
for (int i=0;i<11;i++)
{
a[i+1]=s1[i]-'0';
b[i+1]=s2[i]-'0';
}
int j=11;
while (!a[j]&&j>1)
{
a[j]=9;
j--;
}
if (j==1&&(a[j]==0||a[j]==1))
printf("%lld",doit(b));
else
{
a[j]--;
printf("%lld",doit(b)-doit(a));
}
return 0;
}