BZOJ1026 [SCOI2009]windy数
Description
windy定义了一种windy数。不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。 windy想知道,
在A和B之间,包括A和B,总共有多少个windy数?
Input
包含两个整数,A B。
Output
一个整数
Sample Input
1 10
【输入样例二】
25 50
Sample Output
9
【输出样例二】
20
HINT
【数据规模和约定】
100%的数据,满足 1 <= A <= B <= 2000000000 。
正解:数位DP
解题报告:
第一道完全弄懂的数位DP题,好好总结一下。
首先预处理,用f[i][j]表示i位数中最高位为j的windy数的个数,可以很快处理出来。
我们考虑[a,b]区间完全可以转换为[1,b]-[1,a-1],所以我们先讨论如何统计[1,b]中windy数的个数。
首先我们对于这个区间内的所有数分一分类,(设b位数为len,并且在ABCDEF这个6位数中,A为第len位,F为第1位,a数组表示每一位数是多少,例如a[len]为最高位数字)
第一类 达不到len位的所有情况,那么不足的位数补0,所以我们可以直接对于第len-1到第1位放1到9的任何数的f都算一遍所有windy数,加入答案(就是∑f[i][j],1<=i<len,1<=j<=9)
第二类 达到len位但最高位取不到极限的所有情况,那么我们直接把f[len][i],1<=i<a[len]加入ans即可。
第三类 达到len为且最高位取极限的所有情况,从len-1到1一路累加答案,枚举到第i位时,默认为i+1到len都取得是极限,也就是说,假如一个数962753,我们已经算完了不足6位和有6位但是最高位小于等于9的所有答案,我们首先令此时最高位为9,然后考虑次高位能够选什么,满足windy数的性质就把其f加入答案。这一步操作相当于默认每一个处理完的位都是取最大值了。要注意一点,当我处理到某位发现当前位与前一位已经不满足windy数了,那么后面无论怎样都不可能满足性质,所以直接break就可以了。
1 //It is made by jump~ 2 #include <iostream> 3 #include <cstdlib> 4 #include <cstring> 5 #include <cstdio> 6 #include <cmath> 7 #include <algorithm> 8 #include <ctime> 9 #include <vector> 10 #include <queue> 11 #include <map> 12 #include <set> 13 #ifdef WIN32 14 #define OT "%I64d" 15 #else 16 #define OT "%lld" 17 #endif 18 using namespace std; 19 typedef long long LL; 20 int ans,len; 21 int a[12]; 22 int f[12][12];//f[i][j]表示i位数中最高位为j的方案数 23 24 inline int getint() 25 { 26 int w=0,q=0; 27 char c=getchar(); 28 while((c<'0' || c>'9') && c!='-') c=getchar(); 29 if (c=='-') q=1, c=getchar(); 30 while (c>='0' && c<='9') w=w*10+c-'0', c=getchar(); 31 return q ? -w : w; 32 } 33 34 inline void Init(){ 35 for(int i=0;i<=9;i++) f[1][i]=1;//记得算0 36 for(int i=2;i<=10;i++) 37 for(int j=0;j<=9;j++) 38 for(int k=0;k<=9;k++) 39 if(abs(j-k)>=2) f[i][j]+=f[i-1][k]; 40 } 41 42 inline int solve(int x){ 43 if(x<=0) return 0; 44 ans=0; len=0; while(x) { a[++len]=x%10; x/=10; } 45 //part 1 达不到len位的所有情况 46 for(int i=len-1;i;i--) for(int j=1;j<=9;j++) ans+=f[i][j];//前面位数全部取0,即高位没有数 47 //part 2 达到len位但最高位取不到极限的所有情况 48 for(int i=1;i<a[len];i++) ans+=f[len][i];//计算最高位达不到极限的所有情况,显然都可行 49 //part 3 达到len为且最高位取极限的所有情况,从len-1到1一路累加答案,枚举到第i位时,默认为i+1到len都取得是极限 50 for(int i=len-1;i;i--) {//计算每一位取极限的值,一路往下累加答案 51 for(int j=0;j<a[i];j++) if(abs(a[i+1]-j)>=2) ans+=f[i][j];//似乎可以取0 52 if(abs(a[i+1]-a[i])<2) break;//高位都取极限已经不满足windy数性质,直接break 53 } 54 return ans; 55 } 56 57 inline void work(){ 58 int sx=getint(),sy=getint(); 59 Init(); 60 printf("%d",solve(sy+1)-solve(sx)); 61 } 62 63 int main() 64 { 65 work(); 66 return 0; 67 }