zoukankan      html  css  js  c++  java
  • 数位dp

    数位dp

    给出一个区间,求区间里满足某些条件的数有几个

    • 直接暴力求解
    • 打表+前缀和
    • 数位dp

    当区间范围很大时,时间复杂度需要,无法暴力,只能用数位dp来做

    模板求[1,n]的数字里不含49的个数

    数组(a[i])存放数字n(即区间的端点值)的值,如果n是1234,那么数组就是{4,3,2,1}但是dfs是从最高位1开始的

    数组(dp[i][j])是指当到达第i位,该数字的前缀是j,且满足条件的情况的个数

    int up = limit ? a[pos] : 9;//枚举上限

    指的是枚举的一个上限,如果a[pos] 是 5,那么数字只能枚举(0-5)

    如235百位是0-2枚举,且在这种情况下,枚举十位0-3,个位枚举0-5

    将时间复杂度度从235变为3 * 4 * 6 = 72

    即时间复杂度从O(n)变为各位上的数字加1后的乘积

    最后关于最高位的0的时候,也是可以的,比如033 也就是 33

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<string>
    #define ios_fuck ios::sync_with_stdio(0)
    using namespace std;
    typedef long long ll;
    int a[50];
    ll dp[50][10];
    ll dfs(int pos,int pre,bool limit){//pos当前在数字的第几位,pre是当前数字位的前一位,limit是限制词,是求枚举所需的最大值时用的
        if(pos == -1) return 1;//结束
        if(!limit && dp[pos][pre] != -1) return dp[pos][pre];
        int up = limit ? a[pos] : 9;//枚举上限
        ll tmp = 0;
        for(int i = 0; i <= up; i++){
            if(pre == 4 && i == 9)continue;
            // if(i == 4) continue;
            tmp += dfs(pos - 1,i,limit && i == a[pos]);
        }
        if(!limit) dp[pos][pre] = tmp;
        return tmp;
    }
    ll solve(ll x){
        int pos = 0;
        while(x){
            a[pos++] = x % 10;
            x /= 10;
        }
        return dfs(pos-1,0,true);
    }
    int main(){
        ll ri;
        int t;
        scanf("%d",&t);
        memset(dp,-1,sizeof(dp));
        while(t--){
        	scanf("%lld",&ri);
            printf("%lld
    ",solve(ri));
        }
    
        return 0;
    }
    

    传送门
    windy数
    不含前导零且相邻两个数字之差至少为2的正整数被称为windy数
    那么对于1位数来说都算。
    这里多了一步是处理前导0和只有一位数的情况

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<string>
    #include <cmath>
    #define ios_fuck ios::sync_with_stdio(0)
    using namespace std;
    typedef long long ll;
    int a[50];
    ll dp[50][10];
    ll dfs(int pos,int pre,bool limit){//加入了一个-10,是针对于数字是个位数的
        if(pos == -1) return 1;//结束
        if(!limit && pre >= 0 && dp[pos][pre] != -1) return dp[pos][pre];
        int up = limit ? a[pos] : 9;//枚举上限
        ll tmp = 0;
        for(int i = 0; i <= up; i++){
            if(abs(pre - i) < 2)continue;
            int p;
            if(!i && pre == -10)p = -10;
            else p = i;
            tmp += dfs(pos - 1,p,limit && i == a[pos]);
        }
        if(!limit && pre >= 0) dp[pos][pre] = tmp;
        return tmp;
    }
    ll solve(ll x){
        int pos = 0;
        while(x){
            a[pos++] = x % 10;
            x /= 10;
        }
        return dfs(pos-1,-10,true);
    }
    int main(){
        ll le,ri;
        memset(dp,-1,sizeof(dp));
        while(~scanf("%lld%lld",&le,&ri)){
        	if(le == 0 && ri == 0)break;
            printf("%lld
    ", solve(ri) - solve(le - 1));
        }
    
        return 0;
    }
    
  • 相关阅读:
    PetaPoco 基础操作
    Sql Server实现自动增长
    冒泡排序算法[C++]
    PHP 使用非对称加密算法(RSA)
    Linux的PHP开发环境快速搭建
    PHP常见漏洞攻击简述
    关于计算机编码的笔记
    简述面向对象编程原则和设计模式
    PHP错误处理注册机制
    规范数据库设计
  • 原文地址:https://www.cnblogs.com/Emcikem/p/12198792.html
Copyright © 2011-2022 走看看