BZOJ 1026 windy数
据说这是道数位dp的题,第一反应看到之后也觉得不难,大致思路马上就有了。(其实后来我所看的题解有一个和我想的也差不多,只不过好像觉得那样写太麻烦了,就不想写了,就开始和正道偏离了,所以有了思路之后一定要勇于尝试。)一开始是这样的,先处理出一个数组 f[i][j] 表示 第 i 位为 j 开头的符合题意的数有多少个。(经过这道题,也渐渐总结了一些经验,一开始思考不全面,没有处理好0的情况,所以以后一定要思考全面,把细节都想得差不多了再动手,否则后果真的……)。接下来,就因为0的问题,整个人乱掉了。后来就改成写搜索,然后TLE了(刚刚看了一下别人的题解,如果把爆搜改成记忆化搜索也是能过的,其实我也试着改了,以WA告终)。最后……。
好吧,这是搜索的代码。其实和我写的差不多(我是指思路上,但是这份代码比我一开始写的简洁多了)。
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #define rep(i,j,k) for(int i = j; i <= k; i++) 5 using namespace std; 6 int num[12] = {0}, dp[100][100] = {0}; 7 8 int abs(int x,int y) 9 { 10 if( x > y ) return x - y; else return y - x; 11 } 12 int dfs(int po,int last,int ok,int first) 13 { 14 if( po == 0 ) return 1; 15 if( !first && ok ){ 16 if( dp[po][last] != -1 ) return dp[po][last]; //记搜优化 17 } 18 int cur = first? num[po] : 9; // 这里写的很好。 19 int ans = 0; 20 if( ok ){ 21 rep(i,0,cur){ 22 if( abs(last,i) >= 2 ) ans += dfs(po-1,i,ok,first&&i==cur); // 这里写的很好。 23 } 24 } 25 else { 26 rep(i,0,cur){ 27 ans += dfs(po-1,i,i,first&&i==cur); 28 } 29 } 30 if( !first && ok ) { 31 return dp[po][last] = ans; // 记搜优化 32 } 33 return ans; 34 } 35 36 int dpa(int n) 37 { 38 int tot = 0; 39 while( n ){ 40 num[++tot] = n % 10; 41 n /= 10; 42 } 43 return dfs(tot,0,0,1); 44 } 45 46 int main() 47 { 48 memset(dp,-1,sizeof(dp)); 49 int n, m; scanf("%d %d",&n,&m); 50 cout<<dpa(m)-dpa(n-1)<<endl; 51 return 0; 52 }
接下来是把 i 拆分出来后的代码!(也是我一开始的思路)。可以理解为把 i 划分成几个区间 设 i 最高位可以到 10 的 n 次方, 那么可以把 i 划分成 0 - 10的n次方-1,10的n次方-1到2*10的n次方-1。以此类推直到小于 i 的最高位上的数字。 对 i 的最高位 * 10 的 n 次方 到 i , 进行特殊处理。
1 #include<cstdio> 2 #include<iostream> 3 #define rep(i,j,k) for(int i = j; i <= k; i++) 4 #define down(i,j,k) for(int i = j; i >= k; i--) 5 using namespace std; 6 int f[11][10] = {0}, fabs[12] = {0}; 7 8 int abs(int x,int y) 9 { 10 if( x > y ) return x - y; else return y - x; 11 } 12 13 void init() 14 { 15 fabs[1] = 1; 16 rep(i,2,10){ 17 fabs[i] = fabs[i-1] * 10; 18 } 19 rep(i,0,9) f[1][i] = 1; 20 rep(i,2,10){ 21 rep(j,0,9){ 22 rep(k,0,9){ 23 if( abs(j,k) >= 2 ){ 24 f[i][j] += f[i-1][k]; 25 } 26 } 27 } 28 } 29 } 30 31 int work(int now) 32 { 33 if( !now ) return 0; 34 int tot = 10, ans = 0; 35 while( fabs[tot] > now ) tot--; 36 int cur = now / fabs[tot]; 37 rep(i,1,cur-1) ans += f[tot][i]; 38 down(i,tot-1,1){ 39 rep(j,1,9) ans += f[i][j]; 40 } 41 now %= fabs[tot]; 42 int pre = cur; 43 down(i,tot-1,1){ 44 cur = now / fabs[i]; 45 if( i == 1 ) cur++; 46 for(int j = 0; j <= 9 && j < cur; j++){ 47 if( abs(pre,j) >= 2 ) ans += f[i][j]; 48 } 49 pre = now / fabs[i]; 50 now %= fabs[i]; 51 } 52 return ans; 53 } 54 55 int main() 56 { 57 init(); int n, m; 58 scanf("%d %d", &n, &m); 59 cout<<work(m)-work(n-1)<<endl; 60 return 0; 61 } 62
都不难,加油!多想,勇于尝试。
1026: [SCOI2009]windy数
Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 4518 Solved: 2020
[Submit][Status][Discuss]
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 。