[BZOJ4521][Cqoi2016]手机号码 (数位dp)
题目描述
人们选择手机号码时都希望号码好记、吉利。比如号码中含有几位相邻的相同数字、不含谐音不吉利的数字等。手机运营商在发行新号码时也会考虑这些因素,从号段中选取含有某些特征的号码单独出售。为了便于前期规划,运营商希望开发一个工具来自动统计号段中满足特征的号码数量。
工具需要检测的号码特征有两个:号码中要出现至少 33 个相邻的相同数字;号码中不能同时出现 88 和 44 。号码必须同时包含两个特征才满足条件。满足条件的号码例如:13000988721、23333333333、14444101000。而不满足条件的号码例如:1015400080、10010012022。
手机号码一定是 11 位数,前不含前导的 00 。工具接收两个数 LL 和 RR ,自动统计出 [L,R][L,R] 区间内所有满足条件的号码数量。 LL 和 RR 也是 1111 位的手机号码。
输入输出格式
输入格式:
输入文件内容只有一行,为空格分隔的 22 个正整数 L,RL,R 。
输出格式:
输出文件内容只有一行,为 11 个整数,表示满足条件的手机号数量。
输入输出样例
说明
样例解释:满足条件的号码: 12121285000、 12121285111、 12121285222、 12121285333、 12121285550。
数据范围: 10^{10}\leq L\leq R<10^{11}1010≤L≤R<1011 。
题解
什么鬼?用scanf就会错?要用cin才行?
一道数位dp,我就直接记忆化搜索了
因为是十一位数,所以限制一下第一位不能为0
然后剩下的东西还是看代码好了
1 //minamoto 2 #include<cstdio> 3 #include<cstring> 4 #include<iostream> 5 #define ll long long 6 using namespace std; 7 ll dp[12][12][2][2][4],a,b; 8 int num[12],len; 9 ll dfs(int pos,int Pre,int m1,int m2,int cov,bool flag){ 10 if(m1&&m2) return 0; 11 if(!pos) return cov==3; 12 if(!flag&&(~dp[pos][Pre][m1][m2][cov])) return dp[pos][Pre][m1][m2][cov]; 13 ll res=0;int lim=flag?num[pos]:9; 14 for(int i=pos==len;i<=lim;++i){ 15 switch(cov){ 16 case 0:res+=dfs(pos-1,i,m1|(i==4),m2|(i==8),1,flag&&(i==lim));break; 17 case 3:res+=dfs(pos-1,i,m1|(i==4),m2|(i==8),3,flag&&(i==lim));break; 18 default:res+=dfs(pos-1,i,m1|(i==4),m2|(i==8),i==Pre?cov+1:1,flag&&(i==lim));break; 19 } 20 } 21 if(!flag) dp[pos][Pre][m1][m2][cov]=res; 22 return res; 23 } 24 ll solve(ll x){ 25 len=0; 26 for(;x;x/=10) num[++len]=x%10; 27 return dfs(len,0,0,0,0,1); 28 } 29 int main(){ 30 memset(dp,-1,sizeof(dp)); 31 ios::sync_with_stdio(false); 32 cin>>a>>b; 33 cout<<(a==10000000000?solve(b):solve(b)-solve(a-1))<<endl; 34 return 0; 35 }
深深地明白自己的弱小