SCOI2009 Windy数
这道题是数位DP的入门题。
什么是数位Dp?简单来说,数位DP就是用于解决在一个给定区间之内,有多少个数满足条件的一种DP。其中数的多少和数的大小无关,而与数的结构有关。我们先计算出来[0,r]中符合条件的数,再计算出[0,l-1]中符合条件的数就可以。
以这道题为例,其实在数位之间DP是很好想的,我们枚举相邻的两位,符合条件就把这些方案数全都加上。所以我们设dp[i][j]表示长度为j的数,以j为最高位,有多少个windy数。
那么方程就是dp[i][j] = sigma(dp[i-1][k]),其中abs(k-j)>= 2;
之后的问题是,我们怎么求一段区间之内所有符合的数有多少个呢?
这里提供一种可以计算[0,l)的符合条件数的方法,到时候计算的时候只要相应+1即可。
对于一个数,我们把它每一位存在一个数组的单元中。首先对于每个长度比当前数小的,我们全部加上,因为肯定是符合的(注意第一位不能是0).然后对于位数相同的,我们只要从高位开始,枚举哪一位是小于当前数的。这样一直枚举到最后一位就可以。注意这里必须符合两个条件,第一个是从第二位开始必须保证你当前枚举的每一位都和上一位的绝对值相差大于等于2,否则你就统计了不合法情况。第二个就是你当前的边界本身必须符合条件,否则你相当于一直在统计不合法情况。
这样就可以啦。上代码理解一下。
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> #include<queue> #include<cstring> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar('\n') using namespace std; typedef long long ll; const int M = 1000005; int a,b,dp[20][20],ans,af[20],len; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >='0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } void init() { rep(i,0,9) dp[1][i] = 1;//0~9都是windy数 rep(i,2,10) rep(p,0,9) rep(q,0,9) { if(abs(p-q) < 2) continue; dp[i][p] += dp[i-1][q];//这里是数位Dp } } int solve(int x) { int cur = 0; memset(af,0,sizeof(af));len = 0; while(x) { af[++len] = x % 10; x /= 10;//把一个数拆分 } rep(i,1,len-1) rep(j,1,9) cur += dp[i][j//计算所有位数比它少的]; rep(i,1,af[len]-1) cur += dp[len][i];//第一位 per(i,len-1,1)//从第二位开始 { rep(j,0,af[i]-1) { if(abs(j - af[i+1]) < 2) continue; cur += dp[i][j];//加上合法情况 } if(abs(af[i+1] - af[i]) < 2) break;//如果原数不合法就跳出 } return cur; } int main() { a = read(),b = read(); init(); ans = solve(b+1) - solve(a); printf("%d\n",ans); return 0; }
当你意识到,每个上一秒都成为永恒。