「 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));            //类似前缀和
}

 

posted @ 2018-09-10 16:23  Mystical-W  阅读(144)  评论(0编辑  收藏  举报