本文共 3049 字,大约阅读时间需要 10 分钟。
1. 问题描述:
人们选择手机号码时都希望号码好记、吉利。比如号码中含有几位相邻的相同数字、不含谐音不吉利的数字等。手机运营商在发行新号码时也会考虑这些因素,从号段中选取含有某些特征的号码单独出售。为了便于前期规划,运营商希望开发一个工具来自动统计号段中满足特征的号码数 量。
工具需要检测的号码特征有两个:号码中要出现至少3个相邻的相同数字,号码中不能同时出现8和4。号码必须同时包含两个特征才满足条件。满足条件的号码例如:13000988721、 23333333333、14444101000。而不满足条件的号码例如:1015400080、10010012022。 手机号码一定是11位数,前不含前导的0。工具接收两个数L和R,自动统计出[L,R]区间内所有满足条件的号码数量。L和R也是11位的手机号码。输入描述:
输入文件内容只有一行,为空格分隔的2个正整数L,R。
10^10 ≤ L ≤ R < 10^11输出描述:
输出文件内容只有一行,为1个整数,表示满足条件的手机号数量。
示例1
输入 12121284000 12121285550 输出 5 样例解释 满足条件的号码: 12121285000、 12121285111、 12121285222、 12121285333、 12121285550链接:https://ac.nowcoder.com/acm/problem/19945
来源:牛客网2. 思路分析:
这道题目与牛客中的题目是类似的,都是数位dp的内容,只是这道题目在数中的位上多了几个限制条件:号码中至少需要3个连续相同的数字,不能同时出现4和8,首位不能够为0。主要还是使用递归来尝试每一位可能的数字,在递归的过程中记录下相关的限制条件对应的变量值。我们可以通过在递归方法中传递参数来记录这些限制条件。递归方法需要传递的参数有:当前递归的位置(从高位开始递归)pos,上一次出现的数字last,最开始出现的数字num,是否出现连续3个相同的布尔型变量ok,到目前位置是否出现4-has4,到当前的位置是否出现了8-has8,一个用来记录是否是最高位的最大数字的变量flag,用来控制下一个低位能够填的数字范围,是[0,9]还是[0,x](在数位dp的递归方法中这个变量感觉是必须的),用来标记当前的位置是否是最高位的first变量。可以声明一个六维的列表(列表的维度对应方法中动态变化的参数(python中创建:逆序创建),记忆型的递归都是类似的都是通过在递归过程中动态变化的参数声明对应维度的数组或者列表,每一维度都有其对应的含义这样可以减少子问题的重复求解,而且在数位dp使用递归解决的时候一般都需要这个记忆型列表来减少递归的时间复杂度。递归的主要思想是使用方法中的flag变量来得到当前位置可以填的数字范围,在递归的过程中更新对应参数的变化,使用六维列表来记录递归过程的结果,当发现当前位置的列表值为之前求解过那么直接返回列表对应的值即可。
3. 代码如下:
python代码:
nums = [0] * 13# 六维列表(创建的时候注意括号的嵌套关系)f = [[[[[[-1] * 2 for i in range(2)] for j in range(2)] for k in range(11)] for l in range(12)] for m in range(13)]# num表示前一个数字, last表示最后一个数字(两个变量主要是计算连续的相同的数字的个数), ok记录是否出现过连续相同的三个数字, have4表示到当前位置是否出现过4,, hava8为是否出现过8, flag用来记录是否是最高位的最大数字, first用来标记是否是第一个位置也即最高位, 最高位不能够为0def dfs(pos: int, last: int, num: int, ok: bool, have4: bool, have8: bool, flag: bool, first: int): if pos == 0: return 1 if ok and (not have4 or not have8) else 0 if flag and f[pos][last][num][ok][have4][have8] != -1: return f[pos][last][num][ok][have4][have8] # 三目运算符 x = 9 if flag else nums[pos] res = 0 for i in range(0, x + 1): # 第一个数字不能够为0 if i == 0 and first: continue res += dfs(pos - 1, i, last, ok or (i == last and i == num), have4 or i == 4, have8 or i == 8, flag or i < x, 0) if flag: f[pos][last][num][ok][have4][have8] = res return resdef cal(x: int): pos = 1 while x: nums[pos] = x % 10 x //= 10 pos += 1 return dfs(pos - 1, 0, 0, False, False, False, False, 1)if __name__ == '__main__': l, r = map(int, input().split()) # cal(r)计算[0:r]满足条件的数目, cal(l - 1)计算[0:l - 1]满足条件的数目 print(cal(r) - cal(l - 1))
大佬的c++代码:
//记录最后出现的数字是几 上一位是几 是否已经有三连号 有没有出现8 有没有出现4#includeusing namespace std;#define int long longint f[13][11][13][2][2][2];int a[13];int dp(int pos,int num,int last,bool ok,bool have8,bool have4,bool flag,bool first){ if(pos==0) return ok&&((!have8)||(!have4)); if(flag&&f[pos][num][last][ok][have8][have4]!=-1) return f[pos][num][last][ok][have8][have4]; int x=flag?9:a[pos]; int ans=0; for(int i=0;i<=x;i++) { if(first&&i==0) continue; ans+=dp(pos-1,i,num,ok||(i==num&&i==last),have8||i==8,have4||i==4,flag||i
转载地址:http://ucwg.baihongyu.com/