zoukankan      html  css  js  c++  java
  • 「 Luogu P2657 」 windy数

    # 题目大意

    给出区间 $[a,b]$,求出区间中有多少数满足下列两个条件

    • 不含有前导 $0$。
    • 相邻两个数字之差的绝对值至少是 $2$。

    # 解题思路

    数位 $DP$,用记忆化搜索来实现。设 $dp[i][j]$ 表示现在已经枚举到第 $i$ 位,第 $i+1$ 位是 $j$ 时一共有多少满足条件的数。

    还是直接看代码里的注释吧。

    # 放上代码

    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    using namespace std;
    const int HA = 233;
    //这里要设置为233,不能设置为int_max,会炸
    int n, m, dp[15][15], num[15];
    inline int Abs(int x) {
        return x>0 ? x : -x;
    }
    inline int dfs(int l, int pre, bool limit, bool Zero) {
        if(l == 0) return 1;
        //如果所有的位置都枚举完了,这显然就是一种可行方案
        if(!Zero && !limit && dp[l][pre]) return dp[l][pre];
        //没有前导0和限制是才能用通用答案
        int ans = 0, mx = limit ? num[l] : 9;
        for(int i=0; i<=mx; i++) {
            if(Abs(i-pre) < 2) continue;
            int tmp = (i==0 && Zero) ? -HA : i;
            //如果有前导0并且现在这一位是0,那就设置为一个负数
            ans += dfs(l-1, tmp, limit && (i == mx), tmp==-HA);
            //前面的位有限制并且这一位到达了最高的数字那么限制就可以传递给下一位
        }
        if(!limit && !Zero) dp[l][pre] = ans;
        //没有限制没有前导0才能够成为通用的答案
        return ans;
    }
    inline int solve(int x) {
        //将x分解
        memset(num, 0, sizeof(num));
        int k = 0;
        while (x) {
            num[++k] = x % 10;
            x /= 10;
        }
        return dfs(k, -HA, true, true);                //第k位之前的一定是前导0
    }
    int main() {
        scanf("%d%d", &n, &m);
        printf("%d", solve(m)-solve(n-1));            //类似前缀和
    }
  • 相关阅读:
    题解 nflsoj204 排列
    题解 CF1328 D,E,F Carousel, Tree Queries, Make k Equal
    题解 LOJ3277 「JOISC 2020 Day3」星座 3
    题解 nflsoj464 CF1267K 正睿1225:一个简单的计数技巧
    题解 CF1326F2 Wise Men (Hard Version)
    题解 CF1326E Bombs
    题解 CF1316E and UOJ495:一类结合贪心的背包问题
    仓鼠的DP课 学习笔记
    题解 CF1314B Double Elimination
    固件
  • 原文地址:https://www.cnblogs.com/bljfy/p/9620547.html
Copyright © 2011-2022 走看看