标签:
4521: [Cqoi2016]手机号码
Time Limit: 10 Sec Memory Limit: 512 MB
Submit:
303 Solved: 194
[Submit][Status][Discuss]
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
HINT
Source
Solution
这种数据范围,一眼数位DP,但是不是特别的好搞...
F[i][j][0/1][0/1][0/1][0/1][0/1]表示位数为i,最高位为j,最高位连续两个是否是相同的,是否有连续3个相同的,是否有4,是否有8,前缀和原数前缀的大小关系
枚举这些东西....k1,k2,k3,k4,k5分别表示对应上述0/1
注意&启发:
1.数位DP一般采取预处理,后求和,这里数据范围直接处理非常方便,采用直接处理
2.注意范围$L<=10^{10}$默认10位做的话,不能直接计算Calc(R)-Calc(L-1),这里可以采取讨论,或者处理区间为开区间,计算Calc(R+1)-Calc(L)即可
3.注意状态的枚举,一些零碎的判断需要理清,否则会陷入泥潭(WA了无数,不知所措)
4.如果digit[]正序做更方便可以考虑正序处理,外加此题可以采取特殊的枚举方式
5.更深层次的了解了数位DP,但仍需更多的练习,总体来说,此题是个不错的题目,自己能想到的只是框架,实现仍有些差池
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
long long F[20][15][2][2][2][2][2],l,r;
int p[20],num;
long long cal(long long x)
{
memset(F,0,sizeof(F));
long long ans=0; int len=0,digit[12],a,b,c,d,e;
while(x){digit[++len]=x%10; x/=10;}
reverse(digit+1,digit+len+1);
F[0][10][0][0][0][0][1]=1;
for (int i=0; i<=len-1; i++)
for (int j=0; j<=10; j++)
for (int k1=0; k1<=1; k1++)
for (int k2=0; k2<=1; k2++)
for (int k3=0; k3<=1; k3++)
for (int k4=0; k4<=1; k4++)
for (int k5=0; k5<=1; k5++)
if (F[i][j][k1][k2][k3][k4])
for (int k=0; k<=9; k++)
{
if (k5 && (k>digit[i+1])) continue;
if (k==j) a=1; else a=0;
if (k2==0) b=(k1+a)==2; else b=k2;
if (k3==0) c=(k==4); else c=k3;
if (k4==0) d=(k==8); else d=k4;
if ((c+d)==2) continue;
if (k5 && (k==digit[i+1])) e=1; else e=0;
F[i+1][k][a][b][c][d][e]+=F[i][j][k1][k2][k3][k4][k5];
}
for (int i=0; i<=9; i++)
for (int k1=0; k1<=1; k1++)
for (int k3=0; k3<=1; k3++)
for (int k4=0; (k4<=1)&&(k4+k3<2); k4++)
ans+=F[len][i][k1][1][k3][k4][0];
return ans;
}
int main()
{
scanf("%lld%lld",&l,&r);
printf("%lld\n",cal(r+1)-cal(l));
return 0;
}
昨晚写了道数(S)位(W)DP,结果第二天期中数学(S)物理(W)血崩...虽然1个多月的课,就上了1/4不到...但心里不爽啊o_O
【BZOJ-4521】手机号码 数位DP
标签:
原文地址:http://www.cnblogs.com/DaD3zZ-Beyonder/p/5463911.html