「 Luogu P2657 」 windy数

Posted bljfy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「 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));            //类似前缀和
}

 

以上是关于「 Luogu P2657 」 windy数的主要内容,如果未能解决你的问题,请参考以下文章

luogu P2657 [SCOI2009]windy数 数位dp 记忆化搜索

Luogu P2657 [SCOI2009]windy数

luogu P2657 [SCOI2009]windy数 数位dp入门题

P2657 [SCOI2009]windy数

P2657 [SCOI2009]windy数

P2657 [SCOI2009]windy数