类型:数位DP
传送门:>Here<
题意:不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。求区间$[A, B]$内的windy数个数
解题思路
不看题解一遍A这题也是很水的,怪不得洛谷的难度只有蓝
其实这道题就是需要处理前导零的数位DP。状态还是一样:$dp[i][j]$表示$i$位数开头为$j$的方案数,累积的时候判断abs是否小于2即可
至于前导零的处理,还是和前面一题一样,先做$0 \rightarrow len-1$位的(枚举1),然后做$len$位的即可
总结一下需要处理前导零的数位DP:
由于前导零在且只在第一位时会对答案造成影响,因此只需要特殊处理第一位即可。
在枚举$0 \rightarrow len-1$中的数时,枚举的始终都是第一位,并且是毫无上限限制的,因此只需要从1~9枚举并且直接累积$dp[i][j]$。
对于$len$位处理时,不能碰到上限,并且特判第len位不能取0,其他可以取。
Code
/*By DennyQi 2018.8.13*/ #include <cstdio> #include <queue> #include <cstring> #include <algorithm> #define r read() #define Max(a,b) (((a)>(b)) ? (a) : (b)) #define Min(a,b) (((a)<(b)) ? (a) : (b)) using namespace std; typedef long long ll; const int MAXN = 10010; const int MAXM = 27010; const int INF = 1061109567; inline int read(){ int x = 0; int w = 1; register int c = getchar(); while(c ^ '-' && (c < '0' || c > '9')) c = getchar(); if(c == '-') w = -1, c = getchar(); while(c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar(); return x * w; } int N,M; int dp[12][12],digit[12]; inline int ABS(int x){ return x<0 ? -x : x; } inline void Init(){ for(int j = 0; j <= 9; ++j){ dp[1][j] = 1; } for(int i = 2; i <= 11; ++i){ for(int j = 0; j <= 9; ++j){ for(int k = 0; k <= 9; ++k){ if(ABS(j-k) < 2) continue; dp[i][j] += dp[i-1][k]; } } } } inline int cul(int x){ int y = x; int len = 0, res = 0; while(y > 0){ digit[++len] = y % 10; y /= 10; } digit[len+1] = -100; for(int i = len-1; i; --i){ for(int j = 1; j <= 9; ++j){ res += dp[i][j]; } } for(int i = len; i; --i){ for(int j = 0; j < digit[i]; ++j){ if(i == len && j == 0) continue; if(ABS(digit[i+1]-j) < 2) continue; res += dp[i][j]; } if(ABS(digit[i+1]-digit[i]) < 2) break; } return res; } int main(){ N = r, M = r; Init(); printf("%d", cul(M+1)-cul(N)); return 0; }