• 数位dp题集


    题集见大佬博客

    不要62

    入门题,检验刚才自己有没有看懂

    注意一些细节。

    的确挺套路的

    #include<bits/stdc++.h>
    #define REP(i, a, b) for(register int i = (a); i < (b); i++)
    #define _for(i, a, b) for(register int i = (a); i <= (b); i++)
    using namespace std;
    
    const int MAXN = 15;
    int a[MAXN], dp[MAXN][MAXN][MAXN], len; //dp数组记录除了lead和limit以外其他的东西 
    
    int dfs(int pos, int pre, int lead, int sum, int limit)
    {
        if(pos > len) return sum;
        if(dp[pos][pre][sum] != -1 && !limit && !lead) return dp[pos][pre][sum]; //记忆化的时候记得limit和lead 
        int l = limit ? a[len-pos+1] : 9, res = 0; //注意是倒序存的,所以是len-pos+1
        _for(i, 0, l)
        {
            if(!i && lead) res += dfs(pos + 1, pre, lead, sum, limit && (i == l)); //先看是不是前导0 
            else if(i && lead) res += dfs(pos + 1, i, 0, sum | (i == 4), limit && (i == l)); //看是不是第一位 
            else res += dfs(pos + 1, i, 0, sum | (i == 4) | (pre == 6 && i == 2), limit && (i == l)); //正式处理 
        }
        return (!limit && !lead) ? dp[pos][pre][sum] = res : res; //记忆化的时候记得limit和lead 
    }
    
    int part(int x)
    {
        if(x < 0) return 0; //0的处理 
        memset(dp, -1, sizeof(dp));
        len = 0; int t = x; 
        for(; x; x /= 10) a[++len] = x % 10;
        return t - dfs(1, 0, 1, 0, 1);
    }
    
    int main()
    {
        int n, m;
        while(scanf("%d%d", &n, &m))
        {
            if(n == 0 && m == 0) break;
            printf("%d
    ", part(m) - part(n - 1));
        }
        return 0;
    }

    P2657 [SCOI2009]windy数

    继续套路

    #include<bits/stdc++.h>
    #define REP(i, a, b) for(register int i = (a); i < (b); i++)
    #define _for(i, a, b) for(register int i = (a); i <= (b); i++)
    using namespace std;
    
    const int MAXN = 15;
    int a[MAXN], dp[MAXN][MAXN][MAXN], len;
    
    int dfs(int pos, int pre, int ans, int lead, int limit)
    {
        if(pos > len) return ans;
        if(dp[pos][pre][ans] != -1 && (!limit && !lead)) return dp[pos][pre][ans];
        int l = limit ? a[len-pos+1] : 9, res = 0;
        _for(i, 0, l)
        {
            if(!i && lead) res += dfs(pos + 1, pre, ans, lead, limit & (i == l));
            else if(i && lead) res += dfs(pos + 1, i, ans, 0, limit & (i == l));
            else res += dfs(pos + 1, i, ans & (abs(i - pre) >= 2), 0, limit & (i == l));
        }
        return (!limit && !lead) ? dp[pos][pre][ans] = res : res;
    }
    
    int part(int x)
    {
        if(x == 0) return 1;
        memset(dp, -1, sizeof(dp));
        len = 0; 
        for(; x; x /= 10) a[++len] = x % 10;
        return dfs(1, 0, 1, 1, 1);
    }
    
    int main()
    {
        int a, b;
        scanf("%d%d", &a, &b); 
        printf("%d
    ", part(b) - part(a - 1));
        return 0;
    }

    P2602 [ZJOI2010]数字计数

    第一次这么轻松做出紫题

    一样套模板,爽啊

    #include<bits/stdc++.h>
    #define REP(i, a, b) for(register int i = (a); i < (b); i++)
    #define _for(i, a, b) for(register int i = (a); i <= (b); i++)
    using namespace std;
    
    typedef long long ll;
    const int MAXN = 20;
    const int MAXM = 1e6 + 10;
    ll dp[MAXN][MAXN];
    int a[MAXN], len;
    
    ll dfs(int pos, int ans, int lead, int limit, int key)
    {
        if(pos > len) return ans;
        if(dp[pos][ans] != -1 && (!lead && !limit)) return dp[pos][ans];
        int l = limit ? a[len-pos+1] : 9; ll res = 0;
        _for(i, 0, l)
        {
            if(!i && lead) res += dfs(pos + 1, ans, lead, limit && (i == l), key);
            else res += dfs(pos + 1, ans + (i == key), 0, limit && (i == l), key);
        }
        return (!lead && !limit) ? dp[pos][ans] = res : res;
    }
    
    ll part(ll x, int i)
    {
        memset(dp, -1, sizeof(dp));
        len = 0;
        for(; x > 0; x /= 10) a[++len] = x % 10;
        return dfs(1, 0, 1, 1, i);
    }
    
    inline ll work(ll a, ll b, int i)
    {
        return a ? part(b, i) - part(a - 1, i) : part(b, i) - part(a, i) + (i == 0);
    }
    
    int main()
    {
        ll a, b;
        scanf("%lld%lld", &a, &b);
        _for(i, 0, 9) 
            printf("%lld%c", work(a, b, i), i == 9 ? '
    ' : ' ');
        return 0;
    }

    P3413 SAC#1 - 萌数

    注意数字很大,要用字符串存储

    然后就没啥了,又独立做出紫题

    #include<bits/stdc++.h>
    #define add(a, b) a = (a + b) % mod
    #define REP(i, a, b) for(register int i = (a); i < (b); i++)
    #define _for(i, a, b) for(register int i = (a); i <= (b); i++)
    using namespace std;
    
    const int MAXN = 12;
    const int MAXM = 1e3 + 10;
    const int mod = 1e9 + 7;
    
    int dp[MAXM][MAXN][MAXN][2];
    int a[MAXM], len;
    char s1[MAXM], s2[MAXM];
    
    int dfs(int pos, int pre, int ppre, int ans, int lead, int limit)
    {
        if(pos > len) return ans;
        if(dp[pos][pre][ppre][ans] != -1 && (!lead && !limit)) return dp[pos][pre][ppre][ans];
        int l = limit ? a[len-pos+1] : 9; int res = 0;
        _for(i, 0, l)
        {
            if(!i && lead) add(res, dfs(pos + 1, pre, ppre, ans, lead, limit && (i == l)));
            else if(i && lead) add(res, dfs(pos + 1, i, pre, ans, 0, limit && (i == l)));
            else add(res, dfs(pos + 1, i, pre, ans | (i == pre) | (i == ppre), 0, limit && (i == l)));
        }
        return (!lead && !limit) ? dp[pos][pre][ppre][ans] = res : res;
    }
    
    int part(char* s)
    {
        memset(dp, -1, sizeof(dp));
        len = strlen(s + 1);
        _for(i, 1, len) a[i] = s[len - i + 1] - '0';
        return dfs(1, -1, -1, 0, 1, 1);
    }
    
    int judge(char* s)
    {
        _for(i, 2, strlen(s + 1))
            if(s[i] == s[i-2] || s[i] == s[i-1])
                return 1;
        return 0;
    }
    
    int main()
    {
        scanf("%s%s", s1 + 1, s2 + 1);
        if(s1[1] == 0 && strlen(s1 + 1) == 1) printf("%d
    ", (part(s2) - part(s1) + mod) % mod);
        else printf("%d
    ", (part(s2) - part(s1) + mod + judge(s1)) % mod);
        return 0;
    }

    P4127 [AHOI2009]同类分布

    一开始想的时候当前的数肯定是不能记到答案里的

    当然如果知道模数,一直取模就可以限制范围,就很好了

    但问题是我们并不知道模数,这就比较尴尬了

    就一直卡在这

    然而正解非常暴力,但却是对的。

    既然不知道模数,那就枚举模数

    最后判断一下数字和是不是当前模数且能否整除就好了。

    我为什么没想到呢?

    注意数位dp要开long long

    #include<bits/stdc++.h>
    #define REP(i, a, b) for(register int i = (a); i < (b); i++)
    #define _for(i, a, b) for(register int i = (a); i <= (b); i++)
    using namespace std;
    
    typedef long long ll;
    const int MAXN = 20;
    ll dp[MAXN][MAXN*10][MAXN*10];
    int a[MAXN], len, mod;
    
    ll dfs(int pos, int num, int state, int lead, int limit)
    {
        if(pos > len) return num == mod && state == 0;
        if(dp[pos][num][state] != -1 && (!lead && !limit)) return dp[pos][num][state];
        int l = limit ? a[len-pos+1] : 9; ll res = 0;
        _for(i, 0, l)
        {
            if(!i && lead) res += dfs(pos + 1, num, state, lead, limit && (i == l));
            else if(i && lead) res += dfs(pos + 1, i, i % mod, 0, limit && (i == l));
            else res += dfs(pos + 1, num + i, (state * 10 + i) % mod, 0, limit && (i == l));
        }
        return (!lead && !limit) ? dp[pos][num][state] = res : res;
    }
    
    ll part(ll x)
    {
        len = 0;
        for(; x; x /= 10) a[++len] = x % 10;
        ll res = 0;
        for(mod = 1; mod <= len * 9; mod++)
        {
            memset(dp, -1, sizeof(dp));
            res += dfs(1, 0, 0, 1, 1);
        }
        return res;
    }
    
    int main()
    {
        ll a, b;
        scanf("%lld%lld", &a, &b);
        printf("%lld
    ", !a ? part(b) - part(a) : part(b) - part(a - 1));
        return 0;
    }

    P4317 花神的数论题

    用二进制统计就好了

    不过很奇怪的是一开始我数组开的大小是50

    因为用程序输出1e15最多的位数是47

    但是交上去会WA一个点

    改成51就过了

    以后只要空间剩余多,就多开一些吧,程序中有些神奇的地方可能会比理论上最大值超出一些

    #include<bits/stdc++.h>
    #define mul(a, b) a = (a * b) % mod
    #define REP(i, a, b) for(register int i = (a); i < (b); i++)
    #define _for(i, a, b) for(register int i = (a); i <= (b); i++)
    using namespace std;
    
    typedef long long ll;
    const int MAXN = 51;
    const int mod = 10000007;
    ll dp[MAXN][MAXN];
    int a[MAXN], len;
    
    ll dfs(int pos, int ans, int lead, int limit)
    {
        if(pos > len) return max(1, ans);
        if(dp[pos][ans] != -1 && (!limit && !lead)) return dp[pos][ans];
        int l = limit ? a[len-pos+1] : 1; ll res = 1;
        _for(i, 0, l)
        {
            if(!i && lead) mul(res, dfs(pos + 1, ans, lead, limit && (i == l)));
            else mul(res, dfs(pos + 1, ans + i, 0, limit && (i == l)));
        }
        return (!limit && !lead) ? dp[pos][ans] = res : res;
    }
    
    ll part(ll x)
    {
        len = 0;
        for(; x; x >>= 1) a[++len] = x & 1;
        memset(dp, -1, sizeof(dp));
        return dfs(1, 0, 1, 1);
    }
    
    int main()
    {
        ll a;
        scanf("%lld", &a);
        printf("%lld
    ", part(a));
        return 0;
    }

    总结

    感觉都是套路,掌握套路就好了

    相信自己已经掌握了数位dp

  • 相关阅读:
    面试知识点连接汇总:
    spring学习二:jdbc相关回顾以及spring下dao
    spring学习一:spring入门及相关概念介绍
    日常暖手
    日常暖手
    从破解实例到探讨反调试
    一个有趣的CM
    复健小CM
    Windows下利用py2exe生成静默运行的命令行程序
    获取指定窗口内的文本
  • 原文地址:https://www.cnblogs.com/sugewud/p/9921560.html
Copyright © 2020-2023  润新知