数位dp

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值