• P4124 [CQOI2016]*(数位DP,前导0)


    求n到m之间的吉利手机号,号码中要出现至少 3 个相邻的相同数字;号码中不能同时出现 8和 4。

    这个题需要考虑两个点,一个是前导0,因为有前导0的必然不是手机号,一个是长度必须是11,否则也不是手机号

    至于前导0,可以额外开一个维度,记录当前是否有前导0,但是会时内存x2,本题还不会爆,但是别的题就不一定了:

    #include <bits/stdc++.h>
    
    using namespace std;
    const int N = 1e6 + 5;
    typedef long long LL;
    #define int LL
    int num[32], cnt, dp[32][15][15][2][2][2][2][15];
    
    int dfs(int pos, int pre1, int pre2,int have8,int have4,int state,int limit,int pre0,int len) {
        //pos为dp的第一维 stat为第二维 limit代表当前枚举数字是否达到上限
        if (pos == -1){
            if (len != 11) return 0;
            if (state && !(have8 && have4)) return 1;
            else return 0;
        }
        if (!limit &&(pre1!=-1)&&(pre2!=-1)&& dp[pos][pre1][pre2][have8][state][have4][pre0][len] != -1) return dp[pos][pre1][pre2][have8][state][have4][pre0][len] ;
        //如果没有达到上限,即可以随便枚举,且枚举的情况已经记录过了,就直接返回
        int up;
        if (limit) //如果前面都达到上限了,那么这次也必须只能枚举到上限
            up = num[pos];
        else
            up = 9;
        int res = 0;
        for (int i = 0; i <= up; i++) {
            if (have8 && (i == 4)) continue;
            if (have4 && (i == 8)) continue;
            res += dfs(pos - 1, pre2,i,have8|(i==8),have4|(i==4), state|(pre1==pre2&&pre2==i),limit && i == num[pos],pre0&&(i==0),pre0&&(i==0)?0:len+1);
        }
        if (!limit&&(pre1!=-1)&&(pre2!=-1)) dp[pos][pre1][pre2][have8][state][have4][pre0][len] = res;
        return res;
    }
    
    int solve(LL n) {
        cnt = 0;
        while (n) {
            num[cnt++] = n % 10;
            n /= 10;
        }
        return dfs(cnt - 1,-1,-1, 0,0,0, 1,1,0); //第一个数必须由上限来限制,所以limit=1
    }
    
    signed main() {
        LL n, m;
        scanf("%lld%lld", &n, &m);
        memset(dp, -1, sizeof dp);
        printf("%lld
    ", solve(m) - solve(n - 1));
        return 0;
    }
    

    还有一种写法是直接在枚举当前位置上的数的时候,可以判断是否是第一位,如果是第一位,那么就只能从1开始:

    #include <bits/stdc++.h>
    
    using namespace std;
    const int N = 1e6 + 5;
    typedef long long LL;
    #define int LL
    int num[32], cnt, dp[32][15][15][2][2][2];
    
    int dfs(int pos, int pre1, int pre2,int have8,int have4,int state,int limit) {
        //pos为dp的第一维 stat为第二维 limit代表当前枚举数字是否达到上限
        if (pos == -1){
            if (state && !(have8 && have4)) return 1;
            else return 0;
        }
        if (!limit &&(pre1!=-1)&&(pre2!=-1)&& dp[pos][pre1][pre2][have8][state][have4] != -1) return dp[pos][pre1][pre2][have8][state][have4] ;
        //如果没有达到上限,即可以随便枚举,且枚举的情况已经记录过了,就直接返回
        int up;
        if (limit) //如果前面都达到上限了,那么这次也必须只能枚举到上限
            up = num[pos];
        else
            up = 9;
        int res = 0;
        for (int i = (pos==10)?1:0; i <= up; i++) {
            if (have8 && (i == 4)) continue;
            if (have4 && (i == 8)) continue;
            res += dfs(pos - 1, pre2,i,have8|(i==8),have4|(i==4), state|(pre1==pre2&&pre2==i),limit && i == num[pos]);
        }
        if (!limit&&(pre1!=-1)&&(pre2!=-1)) dp[pos][pre1][pre2][have8][state][have4] = res;
        return res;
    }
    
    int solve(LL n) {
        cnt = 0;
        while (n) {
            num[cnt++] = n % 10;
            n /= 10;
        }
        if (cnt != 11) return 0;
        return dfs(cnt - 1,-1,-1, 0,0,0, 1); //第一个数必须由上限来限制,所以limit=1
    }
    
    signed main() {
        LL n, m;
        scanf("%lld%lld", &n, &m);
        memset(dp, -1, sizeof dp);
        printf("%lld
    ", solve(m) - solve(n - 1));
        return 0;
    }
    
  • 相关阅读:
    Django的路由系统
    Django框架简介
    模块和包
    内置函数——filter和map
    匿名函数
    Djangon 基础总结 汇总 从请求到返回页面的过程,
    Django基础 一
    异常处理
    Sqoop
    Oozie
  • 原文地址:https://www.cnblogs.com/dyhaohaoxuexi/p/14418643.html
Copyright © 2020-2023  润新知