zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    TabActivity 、fragemnt+fragment 和 Fragment+Viewpager 优缺点
    Android App的签名打包(晋级篇)
    Android ProgressBar 进度条荧光效果
    Android UI【android 仿微信、QQ聊天,带表情,可翻页,带翻页拖动缓冲】
    Android UI--自定义ListView(实现下拉刷新+加载更多)
    Android平台下利用zxing实现二维码开发
    自定义组件---图片和文字实现ImageButton效果
    JNI由浅入深_10_JNI 综合开发
    开关按钮实现
    Android Asynctask与Handler的比较,优缺点区别,Asynctask源码
  • 原文地址:https://www.cnblogs.com/dyhaohaoxuexi/p/14418643.html
Copyright © 2011-2022 走看看