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

    数位dp前置条件f[i][j]////一共有i位且最高位是j的方案数

    Description

    如果一个自然数NK进制表示中任意的相邻的两位都不是相邻的数字,那么我们就说这个数是K好数。求LK进制数中K好数的数目。例如K=4,L=2的时候,所有K好数为11、13、20、22、30、31、33 共7个。给定KL,求LK好数的数目。

    Input

    从文件读入数据,第一行为K、L,其中K16L10

    Output

    将结果输出

    Samples

    Input Copy
    4 2
    Output
    7

    Source

    学习数位dp就知道f[i][j]////一共有i位且最高位是j的方案数

    这个题就是K进制的话,就是最高位最对时K-1

    这给题有一个坑就是最高位时1的话就是有k个数,就是得算上0

    #pragma GCC optimize(2)
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    void out(__int128 x) {
        if (x < 0) {
            x = -x;
            putchar('-');
        }
        if (x >= 10) out(x / 10);
        putchar(x % 10 +'0');
    }
    const int maxn=20;
    ll f[maxn][maxn];////一共有i位且最高位是j的方案数
    int kk,l;
    void inint(){
        cin>>kk>>l;
        for(int i=0;i<=kk-1;i++){
            f[1][i]=1;
        }
        for(int i=2;i<=l;i++){
            for(int j=0;j<kk;j++){
                for(int k=0;k<kk;k++){
                    if(abs(j-k)!=1){
                        f[i][j]=(f[i][j]+f[i-1][k]);
                    }
                } 
            }
        }
    }
    int main(){
        inint();
        ll ans=0;
        if(l==1){
            cout<<kk<<endl;
            return 0;
        }
        for(int i=1;i<=kk-1;i++){
            ans=(ans+f[l][i]);
        }
        printf("%lld
    ",ans);
    } 

    视频讲解:

    https://www.bilibili.com/video/BV1yT4y1u7jW?from=search&seid=18102798204929854343

    练习:

    https://vjudge.net/contest/383125#overview

    模板

    #include<vector>
    #include<iostream>
    using namespace std;
    
    const int N = 100;
    
    int f[N][N];
    int u[N];
    
    //根据题目要求来预处理f[i][j]
    void init() {
    
    }
    //计算[0,n]符合f(x)的个数
    int dp(int n) {
        //如果L可以取到0的话,特判-1
        if (n == -1) return 0;
        //特判0
        if (n == 0) return 0 / 1;
    
        //用于把每位的数字提取出来
        vector<int> vt;
        while (n) vt.push_back(n % 10), n /= 10;
    
        //res记录返回值,last保留前缀的信息
        int res = 0, last = 0;
        for (int i = vt.size() - 1; i >= 0; i--) {
            //第一种放法
            for (int j = 0; j < vt[i]; j++) {
                if (true) res += f[][];
            }
            //第二种放法就是vt[i]本身,特判vt[i]是否和法,不合法直接退出就好
            if (false) break;
        }
    
        //返回答案
        return res;
    }
    int main() {
    
        //预处理需要的数据
        init();
    
        int l, r;
        while (cin >> l >> r) {
            //技巧一
            cout << dp(r) - dp(l - 1) << endl;
        }
        return 0;
    }

    //如果是732 先找
    // 第一位先找1 2 3 4 5 6 然后才是这一位是7的时候在递归下一位

    1.A - Amount of Degrees

     

     

    #include<bits/stdc++.h>
    using namespace std;
    int read() {
        int x=0; int f=1;
        char ch=getchar();
        while(ch<'0'||ch>'9') {if(ch=='-') f=-1; ch=getchar();}
        while(ch>='0'&&ch<='9') {x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    const int maxn=31;
    int a[maxn][maxn];
    int n,m,k,b;
    void inint(){
        a[0][0]=1;
        for(int i=0;i<=maxn;i++){
            a[i][0]=1; 
            for(int j=1;j<=i;j++){
                a[i][j]=a[i-1][j]+a[i-1][j-1];
            }
        }
    } 
    int dp(int n){
        if(n==0){
            return 0;
        }
        vector<int>nums;
        while(n){
            nums.push_back(n%b),n/=b;
        }
        int last=0;
        int res=0;
        for(int i=nums.size()-1;i>=0;i--){
            int x=nums[i];    
            if(x){//左边 
                if(x>1){
                    res+=a[i][k-last]+a[i][k-last-1]; 
                    break; 
                }
                else if(x==1){
                    res+=a[i][k-last];
                    last++; 
                    if(last>k){
                        break;
                    }
                }
            }
            if(last>k){
                break;
            }
            if(!i&&last==k){
                res++;
            } 
        }
        return res; 
    }
    int main(){
        inint();
        cin>>n>>m>>k>>b; 
        cout<<dp(m)-dp(n-1)<<endl; 
    } 

    2.B - 数字游戏

    科协里最近很流行数字游戏。某人命名了一种不降数,这种数字必须满足从左到右各位数字成小于等于的关系,如 123123446446。现在大家决定玩一个游戏,指定一个整数闭区间 [a,b][a,b],问这个区间内有多少个不降数。

    Input

    有多组测试数据。每组只含两个数字 a,ba,b,意义如题目描述。

    Output

    每行给出一个测试数据的答案,即 [a,b][a,b] 之间有多少不降数。

    Example

    样例输入

    1 9
    1 19

    样例输出

    9
    18

    Hint

    对于全部数据,1ab23111≤a≤b≤231−1

     

     
    #include<bits/stdc++.h>
    using namespace std;
    int read() {
        int x=0; int f=1;
        char ch=getchar();
        while(ch<'0'||ch>'9') {if(ch=='-') f=-1; ch=getchar();}
        while(ch>='0'&&ch<='9') {x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    const int maxn=15;
    int f[maxn][maxn];//一共有i位且最高位是j的方案数 
    void inint(){
        for(int i=0;i<=9;i++){
            f[1][i]=1;
        }
        for(int i=2;i<maxn;i++){
            for(int j=0;j<=9;j++){
                for(int k=j;k<=9;k++){
                    f[i][j]+=f[i-1][k];
                }
            }
        }
    }
    int dp(int n){
        if(n==0){
            return 1;
        }
        vector<int>nums;
        while(n){
            nums.push_back(n%10),n/=10;
        }
        int last=0,ans=0;
        for(int i=nums.size()-1;i>=0;i--){
            int x=nums[i];
            for(int j=last;j<x;j++){
                ans+=f[i+1][j];
            }
            if(x<last) break;
            last=x;
            if(!i){//递归到最后
                ans++;
            }
        } 
        return ans;
    }
    int main(){
        int l,r;
        inint(); 
        while(cin>>l>>r){  
            cout<<dp(r)-dp(l-1)<<endl;
        }
    }

    C - Windy 数

    原题来自:SCOI 2009

    Windy 定义了一种 Windy 数:不含前导零且相邻两个数字之差至少为 22 的正整数被称为 Windy 数。

    Windy 想知道,在 AA 和 BB 之间,包括 AA 和 BB,总共有多少个 Windy 数?

    Input

    一行两个数,分别为 A,BA,B

    Output

    输出一个整数,表示答案。

    Example

    样例输入 1

    1 10

    样例输出 1

    9

    样例输入 2

    25 50

    样例输出 2

    20


    //如果是732 先找
    // 第一位先找1 2 3 4 5 6 然后才是这一位是7的时候在递归下一位

     
    #include<bits/stdc++.h>
    using namespace std;
    int read() {
        int x=0; int f=1;
        char ch=getchar();
        while(ch<'0'||ch>'9') {if(ch=='-') f=-1; ch=getchar();}
        while(ch>='0'&&ch<='9') {x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    const int maxn=15;
    int f[maxn][maxn];
    void inint(){
        for(int i=0;i<=9;i++){
            f[1][i]=1;
        }
        for(int i=2;i<maxn;i++){
            for(int j=0;j<=9;j++){
                for(int k=0;k<=9;k++){
                    if(abs(j-k)>=2) 
                        f[i][j]+=f[i-1][k];
                }
            }
        }
    }
    int dp(int n){
        if(n==0){
            return 0;
        }
        vector<int>nums;
        while(n){
            nums.push_back(n%10),n/=10;
        }
        int last=-2,ans=0;
        for(int i=nums.size()-1;i>=0;i--){
            int x=nums[i];
            for(int j=i==nums.size()-1;j<x;j++){
                if(abs(j-last)>=2) 
                    ans+=f[i+1][j];
            }
            if(abs(x-last)>=2) last=x;
            else{
                break;
            }
            if(!i){
                ans++;
            }
        }
        for(int i=1;i<nums.size();i++){
            for(int j=1;j<=9;j++){
                ans+=f[i][j];
            }
        } 
        return ans;
    }
    int main(){
        int l,r;
        inint(); 
        while(cin>>l>>r){  
            cout<<dp(r)-dp(l-1)<<endl;
        }
    }

    D - 数字游戏

    由于科协里最近真的很流行数字游戏,某人又命名了一种取模数,这种数字必须满足各位数字之和 

    modNmodN 为 00。现在大家又要玩游戏了,指定一个整数闭区间 [a,b][a,b],问这个区间内有多少个取模数。

    Input

    题目有多组测试数据。每组只含三个数字 a,b,Na,b,N

    Output

    对于每个测试数据输出一行,表示各位数字和 modNmodN 为 00 的数的个数。

    Example

    样例输入

    1 19 9

    样例输出

    2
    Hint

    对于全部数据,1a,b2311,1N<1001≤a,b≤231−1,1≤N<100

    #include<bits/stdc++.h>
    using namespace std;
    int read() {
        int x=0; int f=1;
        char ch=getchar();
        while(ch<'0'||ch>'9') {if(ch=='-') f=-1; ch=getchar();}
        while(ch>='0'&&ch<='9') {x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    const int maxn=11;
    const int M=110;
    int f[maxn][maxn][M];    
    int l,r,p;
    int mod(int x,int y){
        return (x%y+y)%y;
    }
    void inint(){
        memset(f,0,sizeof(f));
        for(int i=0;i<=9;i++){
            f[1][i][i%p]++;
        }
        for(int i=2;i<maxn;i++){
            for(int j=0;j<=9;j++){
                for(int k=0;k<p;k++){
                    for(int x=0;x<=9;x++){
                        f[i][j][k]+=f[i-1][x][mod(k-j,p)];
                    }
                }
            }
        }
    }
    int dp(int x){
        if(x==0){
            return 1;
        }
        vector<int>nums;
        while(x){
            nums.push_back(x%10),x/=10;
        }
        int res=0;
        int last=0;
        for(int i=nums.size()-1;i>=0;i--){
            int x=nums[i];
            for(int j=0;j<x;j++){
                res+=f[i+1][j][mod(-last,p)];
            }
            last+=x;
            if(!i&&last%p==0){
                res++;
            }
        }
        return res;
        
    }
    int main(){
        while(cin>>l>>r>>p){
            inint();
            cout<<dp(r)-dp(l-1)<<endl;
        }
    } 

    E - 不要 62

    杭州人称那些傻乎乎粘嗒嗒的人为 62(音:laoer)。

    杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍,更安全地服务大众。

    不吉利的数字为所有含有 44 或 6262 的号码。例如:62315,73418,8891462315,73418,88914 都属于不吉利号码。但是,6115261152 虽然含有 66 和 22,但不是 6262 连号,所以不属于不吉利数字之列。

    你的任务是,对于每次给出的一个牌照区间号,推断出交管局今后又要实际上给多少辆新的士车上牌照了。

    Input

    输入的都是整数对 n,mn,m,如果遇到都是 00 的整数对,则输入结束。

    Output

    对于每个整数对,输出一个不含有不吉利数字的统计个数,该数值占一行位置。

    Example

    样例输入

    1 100
    0 0

    样例输出

    80
    Hint

    对于全部数据,0<nm<1070<n≤m<107

    #include<bits/stdc++.h>
    using namespace std;
    int read() {
        int x=0; int f=1;
        char ch=getchar();
        while(ch<'0'||ch>'9') {if(ch=='-') f=-1; ch=getchar();}
        while(ch>='0'&&ch<='9') {x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    const int maxn=11;
    int f[maxn][maxn];
    void inint(){
        for(int i=0;i<=9;i++){
            if(i!=4) 
                f[1][i]=1;
        }
        for(int i=2;i<maxn;i++){
            for(int j=0;j<=9;j++){
                if(j==4){
                    continue;
                }
                for(int k=0;k<=9;k++){
                    if(k==4||(j==6&&k==2))
                        continue;
                    f[i][j]+=f[i-1][k];
                }
            }
        }
    }
    int dp(int n){
        if(n==0){
            return 1;
        }
        vector<int>nums;
        while(n){
            nums.push_back(n%10),n/=10;
        }
        int last=0,ans=0;
        for(int i=nums.size()-1;i>=0;i--){
            int x=nums[i];
            for(int j=0;j<x;j++){
                if(j==4||(last==6&&j==2)) continue; 
                ans+=f[i+1][j];
            }
            if(x==4||(last==6&&x==2)) break;
            last=x;
            if(!i){
                ans++;
            }
        } 
        return ans;
    }
    int main(){
        int l,r;
        inint(); 
        while(cin>>l>>r){
            if(l==0&&r==0){
                break;
            }  
            cout<<dp(r)-dp(l-1)<<endl;
        }
    }

     

    #include<vector>#include<iostream>using namespace std;
    const int N = 100;
    int f[N][N];int u[N];
    //根据题目要求来预处理f[i][j]void init() {
    }//计算[0,n]符合f(x)的个数int dp(int n) {//如果L可以取到0的话,特判-1if (n == -1) return 0;//特判0if (n == 0) return 0 / 1;
    //用于把每位的数字提取出来vector<int> vt;while (n) vt.push_back(n % 10), n /= 10;
    //res记录返回值,last保留前缀的信息int res = 0, last = 0;for (int i = vt.size() - 1; i >= 0; i--) {//第一种放法for (int j = 0; j < vt[i]; j++) {if (true) res += f[][];}//第二种放法就是vt[i]本身,特判vt[i]是否和法,不合法直接退出就好if (false) break;}
    //返回答案return res;}int main() {
    //预处理需要的数据init();
    int l, r;while (cin >> l >> r) {//技巧一cout << dp(r) - dp(l - 1) << endl;}return 0;}

    #pragma GCC optimize(2)#include<bits/stdc++.h>using namespace std;typedef long long ll;//不降数inline int read(){    int x=0,f=1;char ch=getchar();    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}const int maxn=15;int f[maxn][maxn];//一共有i位且最高位是j的方案数 void inint(){for(int i=0;i<=9;i++){f[1][i]=1;}for(int i=1;i<maxn;i++){for(int j=0;j<=9;j++){for(int k=j;k<=9;k++){ f[i][j]+=f[i-1][k];//处理那种递增数的个数 }}}}int shudp(int n){if(n==0){return 1;}vector<int>num;while(n){num.push_back(n%10);n/=10;}int last=0,ans=0;for(int i=num.size()-1;i>=0;i--){int x=num[i];for(int j=last;j<x;j++){ans+=f[i+1][j];}if(x<last){break;}last=x;if(i==0){//递归到最后 ans++;}}return ans; }int main(){int l,r;inint();while(cin>>l>>r){cout<<shudp(r)-shudp(l-1)<<endl;} }

  • 相关阅读:
    SseEmitter broken pipe
    沉淀vue相关知识(主要还是个人积累用)
    简述箭头函数基本使用和this指向的问题
    前端渲染和后端渲染,前端路由和后端路由
    .net工程师学习vue的心路历程(三)
    .net工程师学习vue的心路历程(一)
    初始mongodb(一)
    .net工程师学习vue的心路历程(二)
    MySQL自我保护参数
    mongodb副本集添加节点
  • 原文地址:https://www.cnblogs.com/lipu123/p/13333907.html
Copyright © 2011-2022 走看看