Problem Description
不要62
杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer)。
杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍,更安全地服务大众。
不吉利的数字为所有含有4或62的号码。例如:
62315 73418 88914
都属于不吉利号码。但是,61152虽然含有6和2,但不是62连号,所以不属于不吉利数字之列。
你的任务是,对于每次给出的一个牌照区间号,推断出交管局今次又要实际上给多少辆新的士车上牌照了。
Input
输入的都是整数对n、m(0<n≤m<1000000),如果遇到都是0的整数对,则输入结束。
Output
对于每个整数对,输出一个不含有不吉利数字的统计个数,该数值占一行位置。
Sample Input
1 100
0 0
Sample Output
80
#include <iostream>
#include<cmath>
#include<cstdio>
using namespace std;
#define N 1000005
int m,n,pos,tmp;//0<n<=m<N
int dp[20][2],a[N];//第二维是state前一位是不是6会有影响的
//记忆化
int dfs(int pos, int pre, int state, int limit){//limit数位上界(有和无上界) state是上一个数是否为6
int tmp = 0;
if(pos == -1) return 1;
if(!limit && dp[pos][state] != -1) return dp[pos][state];//!!!
int up = limit ? a[pos]:9;
//printf("%d ",up);
for(int i=0;i<=up;i++){
if(i == 4) continue;
if(i == 2 && pre == 6) continue;
tmp += dfs(pos-1, i, i==6, limit && i==a[pos]);
}
if(!limit) dp[pos][state] = tmp;//!!!
return tmp;
}
int solve(int op){
pos = 0;
while(op){
a[pos++] = op % 10;
op /= 10;
}
return dfs(pos-1, -1, 0, 1);
}
int main() {
while(1){
memset(dp,-1,sizeof(dp));
memset(a,0,sizeof(a));
scanf("%d%d",&n,&m);
if(n==0 && m==0) break;
//printf("%d %d \n",solve(n-1), solve(m));
printf("%d\n",solve(m) - solve(n-1));
}
return 0;
}
// 1 62 -->46
相信读者还对这个有不少疑问,笔者认为有必要讲一下记忆化为什么是if(!limit)才行,大致就是说有无limit会出现状态冲突,举例:
约束:数位上不能出现连续的两个1(11、112、211都是不合法的)
假设就是[1,210]这个区间的个数
状态:dp[pos][pre]:当前枚举到pos位,前面一位枚举的是pre(更加前面的位已经合法了),的个数(我的pos从0开始)
先看错误的方法计数,就是不判limit就是直接记忆化
那么假设我们第一次枚举了百位是0,显然后面的枚举limit=false,也就是数位上0到9的枚举,然后当我十位枚举了1,此时考虑dp[0][1],就是枚举到个位,前一位是1的个数,显然dp[0][1]=9;(个位只有是1的时候是不满足的),这个状态记录下来,继续dfs,一直到百位枚举了2,十位枚举了1,显然此时递归到了pos=0,pre=1的层,而dp[0][1]的状态已经有了即dp[pos][pre]!=-1;此时程序直接return dp[0][1]了,然而显然是错的,因为此时是有limit的个位只能枚举0,根本没有9个数,这就是状态冲突了。有lead的时候可能出现冲突,这只是两个最基本的不同的题目可能还要加限制,反正宗旨都是让dp状态唯一
参考网址:
https://blog.csdn.net/konghhhhh/article/details/78346481?utm_source=blogxgwz5