bzoj 1026
很久以前做过的一道数位DP,现在用一种新的解决数位DP的比较一般的方法。
数位DP裸题是:求[L,R]有多少个数。
先转化成求[0,R]有多少个数,然后区间相减即可。
对于[0,R]中的所有数,用0补齐前面的空位,使得每个数的长度都为R的长度。
状态:
dp[pos][0]表示从最高位到pos位,没有顶上界的数的个数
dp[pos][1]表示从最高位到pos位,顶到上界的数的个数(好吧,这个问题中,它一定为1)
然后考虑转移,
dp[pos][0]->dp[pos-1][0](通过枚举pos-1位的数进行转移)
dp[pos][1]->dp[pos-1][0](通过枚举pos-1位,小于该位上界的数进行转移)
dp[pos][1]->dp[pos-1][1](只能在pos-1位填1)
顺推倒推都可以。
dp[1][0]+dp[1][1]就是答案。
对于其它的数位DP,都可以在这个DP上加上需要的状态维,然后解决其它问题。
上面的dp[pos][1]看似没有用(因为始终为1),但是实际上,我们是必须要它的,比如这道题。
我们设dp[pos][top][tail]表示:“比pos高的位,顶上界(top为1)或不顶上界(top为0),最后一位为tail的数的个数(并且要求这个数不为0)“
然后自己YY转移吧(这个不为0的要求和上一道题有点不同,具体看代码)。
新方法:
1 /************************************************************** 2 Problem: 1026 3 User: idy002 4 Language: C++ 5 Result: Accepted 6 Time:0 ms 7 Memory:808 kb 8 ****************************************************************/ 9 10 #include <cstdio> 11 #include <cstring> 12 #define abs(a) ((a)>0?(a):-(a)) 13 14 typedef long long poi; 15 16 poi lf, rg; 17 int aa[20], tot; 18 poi dp[20][2][10]; 19 20 poi calc( poi v ) { // v in [1,oo) 21 for( tot=0; v; v/=10 ) 22 aa[++tot]=v%10; 23 memset( dp, 0, sizeof(dp) ); 24 dp[tot][1][aa[tot]] = 1; 25 for( int i=1; i<aa[tot]; i++ ) 26 dp[tot][0][i] = 1; 27 for( int i=tot; i>=2; i-- ) { 28 for( int c=0; c<=9; c++ ) 29 for( int s=0; s<=9; s++ ) 30 if( abs(s-c)>=2 ) 31 dp[i-1][0][s] += dp[i][0][c]; 32 for( int s=1; s<=9; s++ ) 33 dp[i-1][0][s]++; 34 for( int s=0; s<aa[i-1]; s++ ) 35 if( abs(s-aa[i])>=2 ) 36 dp[i-1][0][s] += dp[i][1][aa[i]]; 37 dp[i-1][1][aa[i-1]] = dp[i][1][aa[i]] && abs(aa[i]-aa[i-1])>=2; 38 } 39 poi rt=dp[1][1][aa[1]]; 40 for( int i=0; i<=9; i++ ) 41 rt += dp[1][0][i]; 42 return rt; 43 } 44 int main() { 45 scanf( "%lld%lld", &lf, &rg ); 46 printf( "%lld\n", calc(rg)-(lf==1?0:calc(lf-1)) ); 47 }
以前的:
1 /************************************************************** 2 Problem: 1026 3 User: idy002 4 Language: C++ 5 Result: Accepted 6 Time:0 ms 7 Memory:804 kb 8 ****************************************************************/ 9 10 #include <cstdio> 11 #include <algorithm> 12 using namespace std; 13 14 struct Form { 15 int n; 16 int b[30]; 17 int src; 18 Form( int a ) { 19 n = 0; 20 src = a; 21 if( a==0 ) { 22 n=1; 23 b[1] = 0; 24 return; 25 } 26 while( a ) { 27 b[++n] = a%10; 28 a/=10; 29 } 30 } 31 }; 32 int dp[10][11]; // dp[0~9][0~10] 33 34 void init() { 35 for( int h=0; h<=9; h++ ) 36 dp[h][1] = 1; 37 for( int l=2; l<=10; l++ ) 38 for( int h=0; h<=9; h++ ) 39 for( int g=0; g<=9; g++ ) 40 if( abs(g-h)>=2 ) 41 dp[h][l] += dp[g][l-1]; 42 } 43 44 int calc( const Form &fm) { // [0,fm.src) 45 int ans = fm.src!=0; // 0 46 for( int l=1; l<fm.n; l++ ) 47 for( int h=1; h<=9; h++ ) { 48 ans += dp[h][l]; 49 //printf( "(h=%d,l=%d)\n", h, l ); 50 } 51 for( int h=1; h<fm.b[fm.n]; h++ ) { 52 ans += dp[h][fm.n]; 53 //printf( "(h=%d,l=%d)\n", h, fm.n ); 54 } 55 for( int l=fm.n-1; l>=1; l-- ) { 56 for( int h=0; h<fm.b[l]; h++ ) 57 if( abs(h-fm.b[l+1])>=2 ) { 58 ans += dp[h][l]; 59 //printf( "(h=%d,l=%d)\n", h, l ); 60 } 61 if( abs(fm.b[l]-fm.b[l+1])<2 ) break; 62 } 63 return ans; 64 } 65 66 int main() { 67 // freopen( "windy.in", "r", stdin ); 68 // freopen( "windy.out", "w", stdout ); 69 int A, B, ans; 70 init(); 71 scanf( "%d%d", &A, &B ); 72 ans = calc(Form(B+1))-calc(Form(A)); 73 printf( "%d\n", ans ); 74 }