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

原文地址:https://www.cnblogs.com/bljfy/p/9620547.html

时间: 2024-09-29 12:28:20

「 Luogu P2657 」 windy数的相关文章

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

题目链接 luogu P2657 [SCOI2009]windy数 题解 我有了一种所有数位dp都能用记忆话搜索水的错觉 代码 #include<cstdio> #include<algorithm> inline int read() { int x = 0,f = 1; char c = getchar(); while(c < '0' || c > '9') c = getchar(); while(c <= '9' && c >= '

Luogu P2657 [SCOI2009]windy数

题目 首先我们可以非常轻松地预处理出\(f_{i,j}\)表示一个最高位为\(i\)位且该位为\(j\)的windy数的个数. 然后我们可以利用经典容斥把答案变成求\([1,x]\)的windy数个数. 设\(x\)有\(len\)位,从低到高位分别是\(a_1,\cdots,a_{len}\) 首先我们把位数小于\(len\)的答案求出. 然后求出位数等于\(len\)且首位小于\(a_{len}\)的答案. 然后我们从大到小枚举\(len\sim i-1\)位相等,\(i\)位不等,枚举第\

P2657 [SCOI2009]windy数

题目描述 windy定义了一种windy数.不含前导零且相邻两个数字之差至少为2的正整数被称为windy数. windy想知道, 在A和B之间,包括A和B,总共有多少个windy数? 输入输出格式 输入格式: 包含两个整数,A B. 输出格式: 一个整数 输入输出样例 输入样例#1: 1 10 输出样例#1: 9 输入样例#2: 25 50 输出样例#2: 20 说明 100%的数据,满足 1 <= A <= B <= 2000000000 . Solution: 本题显然数位$DP$,

「SCOI2009」windy数

传送门 Luogu 解题思路 数位 \(\text{DP}\) 设状态 \(dp[now][las][0/1][0/1]\) 表示当前 \(\text{DP}\) 到第 \(i\) 位,前一个数是 \(las\),有没有顶到上界,有没有前导零的答案. 转移十分显然. 细节注意事项 咕咕咕 参考代码 #include <algorithm> #include <iostream> #include <cstring> #include <cstdlib> #i

「Luogu 2367」语文成绩

更好的阅读体验 Portal Portal1: Luogu Description 语文老师总是写错成绩,所以当她修改成绩的时候,总是累得不行.她总是要一遍遍地给某些同学增加分数,又要注意最低分是多少.你能帮帮她吗? Input 第一行有两个整数\(n\),\(p\),代表学生数与增加分数的次数. 第二行有\(n\)个数,\(a_1 \sim a_n\),代表各个学生的初始成绩. 接下来\(p\)行,每行有三个数,\(x\),\(y\),\(z\),代表给第\(x\)个到第\(y\)个学生每人增

「Luogu P3178」[HAOI2015]树上操作

有一棵点数为 \(N\) 的树,以点 \(1\) 为根,且树点有边权.然后有 \(M\) 个操作,分为三种: 操作 1 :把某个节点 \(x\) 的点权增加 \(a\) . 操作 2 :把某个节点 \(x\) 为根的子树中所有点的点权都增加 \(a\) . 操作 3 :询问某个节点 \(x\) 到根的路径中所有点的点权和. Luogu 分析 我们把树上问题利用 \(dfs\) 序转化成序列问题然后直接上线段树解决即可. 考虑将线段树的每个叶子结点设为在原树上的点到根的点权和.对于单点修改,当前结

「Luogu P6101」[EER2]出言不逊

Portal Portal1: Luogu Solution 模拟,先找到在读入字符串内出现次数最多的字符,记录个数,然后以 \(2\) 为指数在现有长度上递增,就可以算出答案. 但是long long会溢出,所以要判断一下,如mx + mx < mx说明已经溢出了,然后就退出答案做个标记,输出的时候\(+1\),否则会死循环,至于__int128,我没试过. 代码纯属是为过而过,没什么可看的. Code #include<bits/stdc++.h> #pragma GCC optim

「Luogu 1821」[USACO07FEB]银牛派对Silver Cow Party

更好的阅读体验 Portal Portal1: Luogu Portal2: POJ Description One cow from each of N farms \((1 \le N \le 1000)\) conveniently numbered \(1 \cdots N\) is going to attend the big cow party to be held at farm #X \((1 \le X \le N)\). A total of \(M (1 \le M \l

$P2657\ [SCOI2009]\ windy$数

属于数位\(DP\)入门级别的题目,但我做这类题不多,还是要总结一下这道经典题目 \(Description\) 题面 给定\(a,b\),求\([a,b]\)区间有多少个数满足:任意两个相邻数位之间的差的绝对值\(>=2\) \(a,b<=1e12\) \(Solution\) 数位\(DP\)的基本思想是一个一个数确定,逼近到边界 数位\(DP\)一般设计状态为\(dp[i][s]\)表示当前考虑到第\(i\)位(从最低位编号),当前位置或附近位置状态为\(s\)的方案数. 有时候需要预处