zoukankan      html  css  js  c++  java
  • 数位DP Ⅰ

    我的使用的数位DP与一般的DFS实现的不同,首先数位DP的核心思想看这张树图。从数N的高位到低位处理,首先由于第一位最大是an-1,枚举第一位是(0)~(a)(n-1)(-1)时后面的位数都不受限制,此时我们可以预处理一个数组f[i][j],表示以j开始长度为i(也可能是组合数或者其它)的满足条件数的个数(或某种值),左边枚举的可以直接用f数组求,然后当前位固定为(a)(n-1),再去处理下一位...直到固定的某一个数不符合条件退出,如果能够遍历到最后一位,如果可以再把N算上。

    本篇的题是数位DP求区间满足条件的个数。

    1081. 度的数量

    求给定区间 [X,Y] 中满足下列条件的整数个数:这个数恰好等于K个互不相等的B的整数次幂之和。
    例如,设 X=15,Y=20,K=2,B=2,则有且仅有下列三个数满足题意:
    17=2^4 + 2^0
    18=2^4 + 2^1
    20=2^4 + 2^2
    输入格式
    第一行包含两个整数 X 和 Y,接下来两行包含整数 K 和 B。
    输出格式
    只包含一个整数,表示满足条件的数的个数。
    数据范围
    1≤X≤Y≤2^31−1,
    1≤K≤20,
    2≤B≤10
    输入样例:
    15 20
    2
    2
    输出样例:
    3

    即B进制下表示有k个1其它为0,我们可以预处理一个数组f[i][j]表示长度为i中选择j位的算法

    #include<bits/stdc++.h>
    using namespace std;
    const int N=40;
    int f[N][N],k,b;
    int dp(int num){
        vector<int> nums;
        while(num) nums.push_back(num%b),num/=b;
        int res=0,last=0;
        for(int i=nums.size()-1;i>=0;--i){
            int x=nums[i];
            if(x) { //x>=1 
                res+=f[i][k-last]; //填0
                if(x>1){
                    res+=f[i][k-last-1]; //填1
                    break; //x大于1 后面的不满足都条件
                }
                else {
                    last++; //填1并走到下一位
                    if(last>k) break;
                }
            }
            if(last==k&&i==0) res++;
        }
        return res;
    }
    int main(){
        for(int i=0;i<N;++i){
            for(int j=0;j<=i;++j){
                if(j==0) f[i][j]=1;
                else f[i][j]=f[i-1][j]+f[i-1][j-1];
            }
        }
        int l,r;
        cin>>l>>r>>k>>b;
        cout<<dp(r)-dp(l-1)<<endl;
        return 0;
    }
    

    1082. 数字游戏

    科协里最近很流行数字游戏。
    某人命名了一种不降数,这种数字必须满足从左到右各位数字呈非下降关系,如 123,446。
    现在大家决定玩一个游戏,指定一个整数闭区间 [a,b],问这个区间内有多少个不降数。
    输入格式
    输入包含多组测试数据。
    每组数据占一行,包含两个整数 a 和 b。
    输出格式
    每行给出一组测试数据的答案,即 [a,b] 之间有多少不降数。
    数据范围
    1≤a≤b≤231−1
    输入样例:
    1 9
    1 19
    输出样例:
    9
    18

    预处理f数组,f[i][j]表示长度为i,第一个数为j的非降序数列个数

    #include<bits/stdc++.h>
    using namespace std;
    const int N=15;
    int f[N][N]; //f[i][j] 长度位i,第一个数字是j
    int dp(int num){
        if(num==0) return 1;
        vector<int> nums;
        while(num) nums.push_back(num%10),num/=10;
        int res=0,last=0;
        for(int i=nums.size()-1;i>=0;--i){
            int x=nums[i];
            for(int j=last;j<x;++j){
                res+=f[i+1][j];
            }
            if(last>x) break;
            last=x;
            if(i==0) res++;
        }
        return res;
    }
    int main(){
        for(int i=0;i<10;++i) f[1][i]=1;
        for(int i=2;i<N;++i){
            for(int j=0;j<10;++j){
                for(int k=j;k<10;++k){
                        f[i][j]+=f[i-1][k];
                }
            }
        }
        int l,r;
        while(cin>>l>>r)  cout<<dp(r)-dp(l-1)<<endl;
    }
    

    1085. 不要62

    杭州人称那些傻乎乎粘嗒嗒的人为 62(音:laoer)。
    杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍,更安全地服务大众。
    不吉利的数字为所有含有 4 或 62 的号码。例如:62315,73418,88914 都属于不吉利号码。但是,61152 虽然含有 6 和 2,但不是 连号,所以不属于不吉利数字之列。
    你的任务是,对于每次给出的一个牌照号区间 [n,m],推断出交管局今后又要实际上给多少辆新的士车上牌照了。
    输入格式
    输入包含多组测试数据,每组数据占一行。
    每组数据包含一个整数对 n 和 m。
    当输入一行为“0 0”时,表示输入结束。
    输出格式
    对于每个整数对,输出一个不含有不吉利数字的统计个数,该数值占一行位置。
    数据范围
    1≤n≤m≤109
    输入样例:
    1 100
    0 0
    输出样例:
    80

    f[i][j]表示长度为i,第一个数字为j的不包含62和4的数列个数

    #include<bits/stdc++.h>
    using namespace std;
    const int N=30;
    int f[N][N];
    int dp(int num){
        if(num==0) return 1;
        vector<int> nums;
        while(num) nums.push_back(num%10),num/=10;
        int res=0,last=0;
        for(int i=nums.size()-1;i>=0;--i){
            int x=nums[i];
            for(int j=0;j<x;++j){
                if(last==6&&j==2) continue;
                res+=f[i+1][j];
            }
            if((last==6&&x==2)||x==4) break;
            last=x;
            if(i==0) res++;
        }
        return res;
    }
    int main(){
        for(int i=0;i<10;++i) f[1][i]=1;
        f[1][4]=0;
        for(int i=2;i<N;++i){
            for(int j=0;j<10;++j){
                if(j==4) continue;
                for(int k=0;k<10;++k){
                    if(k==4) continue;
                    if(j==6&&k==2) continue;
                    f[i][j]+=f[i-1][k];
                }
            }
        }
        //cout<<f[2][0]<<endl;
        int l,r;
        while(cin>>l>>r,!(!l&&!r))  cout<<dp(r)-dp(l-1)<<endl;
        return 0;
    }
    

    1083. Windy数

    Windy 定义了一种 Windy 数:不含前导零且相邻两个数字之差至少为 2 的正整数被称为 Windy 数。
    Windy 想知道,在 A 和 B 之间,包括 A 和 B,总共有多少个 Windy 数?
    输入格式
    共一行,包含两个整数 A 和 B。
    输出格式
    输出一个整数,表示答案。
    数据范围
    1≤A≤B≤2×109
    输入样例1:
    1 10
    输出样例1:
    9
    输入样例2:
    25 50
    输出样例2:
    20

    f[i][j]表示长度为i,第一个数字为j的Windy数列个数。注意其他题对于枚举前导0没有限制,例如00014不是windy数,14是windy数,所以在枚举最高位是0时分情况:统计第一位为0的情况+第二位为0的情况...+0的情况

    #include<bits/stdc++.h>
    using namespace std;
    const int N=40;
    int f[N][N]; //f[i][j] 长度为i,第一个数字为j
    int dp(int num){
        if(num==0) return 1;
        vector<int> nums;
        while(num) nums.push_back(num%10),num/=10;
        int res=0,last=100;
        for(int i=nums.size()-1;i>=0;--i){
            int x=nums[i];
            for(int j=0;j<x;++j){
                if(j==0&&(int)nums.size()-1==i){
                    for(int l=0;l<(int)nums.size();++l){
                        for(int k=1;k<10;++k) res+=f[l][k];
                    }
                    res++;
                }
                else if(abs(j-last)>=2) res+=f[i+1][j];
            }
            if(abs(last-x)>=2) last=x;
            else break;
            if(i==0) res++;
        }
        //cout<<res<<endl;
        return res;
    }
    int main(){
        for(int i=0;i<10;++i) f[1][i]=1;
        for(int i=2;i<N;++i){
            for(int j=0;j<10;++j){
                for(int k=0;k<10;++k){
                    if(abs(j-k)>=2) f[i][j]+=f[i-1][k];
                }
            }
        }
        //cout<<f[2][0]<<endl;
        int l,r;
        cin>>l>>r;
        cout<<dp(r)-dp(l-1)<<endl;
        return 0;
    }
    
  • 相关阅读:
    uniapp中的跳转传参
    图解排序算法(三)之堆排序
    serverlesss
    kvm
    用户态和内核态的理解和区别
    MySQL优化十大技巧
    不懂数据库索引的底层原理?那是因为你心里没点b树
    让你的 Linux 命令骚起来
    史上最简约的vi教程
    mysql 四种隔离级别
  • 原文地址:https://www.cnblogs.com/jjl0229/p/12659395.html
Copyright © 2011-2022 走看看