数位dp总结

HDU2089 求x在【L, R】满足 , x中不含数字4 ,且不含62 。 的个数

typedef long long  LL  ;

const  int Max_N = 20 ;
LL    dp[Max_N][Max_N] ;
int    bit[Max_N] ;

LL    DP(int pos , int pre , int isend){
       if(pos == -1)  return  1 ;
       if(!isend && dp[pos][pre] != -1)  return dp[pos][pre] ;
       int d = isend ? bit[pos] : 9 ;
       LL  ans = 0 ;
       for(int i = 0 ; i <= d ; i++){
             if(i == 4)  continue ;
             if(pre == 6 && i == 2) continue ;
             ans += DP(pos-1 , i , isend && i==d) ;
       }
       if(!isend)  dp[pos][pre] = ans ;
       return ans ;
}

LL    Ans(LL  x){
       int  Len  = 0  ;
       while(x){
             bit[Len++] = x % 10 ;
             x /= 10 ;
       }
       return  DP(Len-1 , 0 , 1) ;
}

int    main(){
       memset(dp , -1 , sizeof(dp)) ;
       LL l , r  ;
       while(cin>>l>>r){
              if(l == 0 && r == 0) break ;
              cout<< Ans(r) - Ans(l-1) <<endl ;
       }
       return 0 ;
}

 

HDU3555  求x在【L, R】满足 , x中含49 。 的个数.

法1 : n+1 - 不含49 。

const int   Max_N = 20 ;
LL    dp[Max_N][10] ;
int   bit[Max_N] ;

LL    DP(int pos , int pre , int isend){
      if(pos == -1)  return 1 ;
      if(!isend && dp[pos][pre] != -1)  return dp[pos][pre] ;
      LL ans = 0  ;
      int d = isend ? bit[pos] : 9 ;
      for(int i = 0 ; i <= d ; i++){
            if(pre == 4 && i == 9)  continue ;
            ans += DP(pos-1 , i , isend && i == d) ;
      }
      if(!isend) dp[pos][pre] = ans ;
      return ans ;
}

LL    Ans(LL x){
      int  Len = 0  ;
      while(x){
            bit[Len++] = x % 10 ;
            x /= 10 ;
      }
      return DP(Len-1 , 0 , 1) ;
}

int  main(){
     memset(dp , -1 , sizeof(dp)) ;
     LL n ;
     int t ;
     cin>>t  ;
     while(t--){
           cin>>n  ;
           cout<< n + 1 - Ans(n) << endl ;
     }
     return 0 ;
}

法二 , 加1维 。 直接算。

const int   Max_N = 20 ;
LL    dp[Max_N][2][10] ;
int   bit[Max_N] ;

LL    DP(int pos , int  have , int pre , int isend){
      if(pos == -1)  return have ;
      if(!isend && dp[pos][have][pre] != -1)  return dp[pos][have][pre] ;
      LL ans = 0  ;
      int d = isend ? bit[pos] : 9 ;
      for(int i = 0 ; i <= d ; i++){
            if(pre == 4 && i == 9)
                 ans += DP(pos-1 , 1 , i , isend && i==d) ;
            else
                 ans += DP(pos-1 , have ,i , isend && i == d) ;
      }
      if(!isend) dp[pos][have][pre] = ans ;
      return ans ;
}

LL    Ans(LL x){
      int  Len = 0  ;
      while(x){
            bit[Len++] = x % 10 ;
            x /= 10 ;
      }
      return DP(Len-1 , 0 , 0 , 1) ;
}

int  main(){
     memset(dp , -1 , sizeof(dp)) ;
     LL n ;
     int t ;
     cin>>t  ;
     while(t--){
           cin>>n  ;
           cout<< Ans(n) << endl ;
     }
     return 0 ;
}


HDU 3652  求x在【L, R】满足 , x中含13, 且是13的倍数 。 的个数 。

const int   Max_N = 20 ;
LL    dp[Max_N][2][10][13] ;
int   bit[Max_N] ;

LL    DP(int pos , int  have , int pre , int sum , int isend){
      if(pos == -1)  return have && sum == 0 ;
      if(!isend && dp[pos][have][pre][sum] != -1)  return dp[pos][have][pre][sum] ;
      LL ans = 0  ;
      int d = isend ? bit[pos] : 9 ;
      for(int i = 0 ; i <= d ; i++){
            if(pre == 1 && i == 3)
                 ans += DP(pos-1 , 1 , i ,  (sum*10+i) %13 , isend && i==d) ;
            else
                 ans += DP(pos-1 , have ,i , (sum*10+i) %13 , isend && i==d) ;
      }
      if(!isend) dp[pos][have][pre][sum] = ans ;
      return ans ;
}

LL    Ans(LL x){
      int  Len = 0  ;
      while(x){
            bit[Len++] = x % 10 ;
            x /= 10 ;
      }
      return DP(Len-1 , 0 , 0 , 0 , 1) ;
}

int  main(){
     memset(dp , -1 , sizeof(dp)) ;
     LL n ;
     while(cin>>n){
           cout<< Ans(n) << endl ;
     }
     return 0 ;
}


fzu 2113  [L , R] 之间的整数包含多少个1 。

LL    DP(int pos , int one , int isend){
      if(pos == -1)  return (LL)one ;
      if(!isend && dp[pos][one] != -1) return dp[pos][one] ;
      LL  ans = 0 ;
      int d = isend ? bit[pos] : 9 ;
      for(int i = 0  ; i <= d ; i++)
           ans += DP(pos-1 , one + (i==1) , isend&&i==d) ;
      if(!isend) dp[pos][one] = ans ;
      return ans ;
}

HDU 4734 题意: 看懂f(x)函数之后,求[0,B]内的f(x)的值小于f(a)的有多少个。 

const int Max_N = 10008 ;
int   dp[10][Max_N] ;
int   bit[10] ;

int   f(int x){
      int s = 0  , i = 0 ;
      while(x){
           s += (x%10) * (1<<i) ;
           i++ ;
           x /= 10 ;
      }
      return s  ;
}

int   DP(int pos , int num , int isend){
      if(pos == -1) return  num >= 0 ;
      if(num < 0)  return 0 ;
      if(!isend && dp[pos][num] != -1) return dp[pos][num] ;
      int d =  isend? bit[pos] : 9 ;
      int  ans = 0 ;
      for(int  i = 0 ; i <= d ; i++){
          ans += DP(pos-1 , num - i*(1<<pos) , isend && i==d) ;
      }
      if(!isend) dp[pos][num] = ans ;
      return ans ;
}

int   Ans(int A , int B){
      int i ,  Len = 0 ;
      while(B){
            bit[Len++] = B%10 ;
            B /= 10 ;
      }
      return DP(Len-1 , f(A) , 1) ;
}

int  main(){
     int cas , T  , A , B ;
     memset(dp , -1 , sizeof(dp)) ;
     cin>>T ;
     for(cas = 1 ; cas <= T ; cas++){
          scanf("%d%d" ,&A ,&B) ;
          printf("Case #%d: %d\n" , cas , Ans(A , B)) ;
     }
     return  0 ;
}

zoj  3416  求【L, R】 之间满足 一个数是Balanced 的个数。Balanced指这个数的力矩为0 。 如 4139 选支点为3  4*2 + 1*1 = 9*1  。

const  int Max_N = 20 ;
LL   dp[Max_N][Max_N][2000] ;
int   bit[Max_N] ;

LL   DP(int pos , int center , int sum , int isend){
      if(pos == -1)  return sum == 0 ;
      if(sum < 0) return 0  ;
      if(!isend && dp[pos][center][sum] != -1) return  dp[pos][center][sum] ;
      int d = isend ? bit[pos] : 9 ;
      LL ans = 0 ;
      for(LL i = 0 ; i <= d ; i++)
          ans += DP(pos-1 , center , sum+i*(pos-center) , isend && i==d) ;
      if(!isend) dp[pos][center][sum] = ans ;
      return ans  ;
}

LL   Ans(LL x){
      int Len = 0 ;
      while(x){
           bit[Len++] = x%10 ;
           x /= 10 ;
      }
      LL  ans = 0 ;
      for(int center = 0 ; center < Len ; center++) //枚举支点
          ans += DP(Len-1 , center , 0 , 1)  ;
      return ans - (Len-1) ;
}

int   main(){
      memset(dp , -1 ,sizeof(dp)) ;
      int t  ;
      LL l , r ;
      cin>>t ;
      while(t--){
           scanf("%lld%lld" ,&l ,&r) ;
           printf("%lld\n" ,Ans(r) - Ans(l-1)) ;
      }
      return 0 ;
}

 

codeforces 55D 求x在[L, R], 满足x是其每位非0数的倍数。的个数

关键之处,降维。

const  int Max_N = 20  ;
LL  dp[Max_N][2520][50] ;
int  bit[Max_N] ;
int  Maxlcm ;

int  gcd(int x , int y){
     return y == 0 ? x : gcd(y , x%y) ;
}
int  lcm(int x , int y){
     return x / gcd(x ,y) * y ;
}
map<int , int>id  , _hash ;

LL   DP(int pos , int  sum , int lcmid , int isend){
     if(pos == -1) return sum % id[lcmid] == 0 ;
     if(!isend && dp[pos][sum][lcmid] != -1) return dp[pos][sum][lcmid] ;
     int d = isend ? bit[pos] : 9 ;
     LL ans = 0  ;
     for(int i = 0 ; i <= d ; i++){
         if(i == 0)  
            ans += DP(pos-1 , sum*10%Maxlcm , lcmid , isend && i==d) ;
         else        
            ans += DP(pos-1 , (sum*10+i)%Maxlcm , _hash[lcm(id[lcmid] ,i)] , isend && i==d) ;
     }
     if(!isend) dp[pos][sum][lcmid] = ans ;
     return ans ;
}

LL   Ans(LL x){
     int Len = 0 ;
     while(x){
           bit[Len++] = x%10 ;
           x /= 10 ;
     }
     return DP(Len-1 , 0 , 0 , 1) ;
}

int  main(){
     int indx = 0 ;
     Maxlcm = 1 ;
     for(int i = 1 ; i <= 9 ; i++)  Maxlcm = lcm(Maxlcm , i) ;
     id.clear() ;
     _hash.clear() ;
     for(int i = 1 ; i <= Maxlcm ; i++){
          if(Maxlcm % i == 0){
                _hash[i] = indx ;
                id[indx++] = i ;
          }
     }
     memset(dp , -1 , sizeof(dp)) ;
     int t  ;
     LL l , r  ;
     cin>>t ;
     while(t--){
          cin>>l>>r ;
          cout<< Ans(r) - Ans(l-1) <<endl ;
     }
     return 0 ;
}


FZU 2109   求x在[L, R], 满足x的 奇数位的的>=偶数位数  的个数

const  int  Max_N = 12 ;
int    dp[Max_N][Max_N][Max_N] ;
int    bit[Max_N] ;

int    DP(int pos , int id , int pre , int isend){
       if(pos == -1) return 1;
       if(!isend && dp[pos][id][pre] != -1) return dp[pos][id][pre] ;
       int ans = 0 ;
       int d = isend ? bit[pos] : 9  ;
       if(id&1){
            for(int i = pre ; i <= d ; i++)
                 ans += DP(pos-1 , id+1 , i , isend&&i==d) ;
       }
       else{
            for(int i = 0 ; i <= min(pre , d) ; i++)
                 ans += DP(pos-1 , id+1 , i , isend&&i==d) ;
       }
       if(!isend)  dp[pos][id][pre] = ans ;
       return ans ;
}

int    Ans(int  x){
       int Len = 0 ;
       while(x){
            bit[Len++] = x % 10 ;
            x /= 10 ;
       }
       return DP(Len-1 , 0 , 9 , 1) ;
}

int  main(){
     memset(dp , -1 , sizeof(dp)) ;
     int i , t , l  ,r ;
     cin>>t ;
     while(t--){
           scanf("%d%d" ,&l ,&r) ;
           printf("%d\n" , Ans(r) - Ans(l-1)) ;
     }
     return 0 ;
}

UESTC 250  处理前导,不含前导零且相邻两个数字之差至少为2的正整数 。

LL    DP(int pos , int pre , bool islead ,  int isend){
      if(pos == -1) return 1 ;
      if(!isend && dp[pos][pre] != -1) return dp[pos][pre] ;
      LL ans = 0  ;
      int d = isend ? bit[pos] : 9 ;
      for(int i = 0 ; i <= d ; i++){
            if(islead && i==0)
                 ans += DP(pos-1 , 10 , 1 , isend&&i==d) ;
            else{
                 if(pre == 10)
                      ans += DP(pos-1 , i , 0 , isend&&i==d) ;
                 else{
                      if(abs(i-pre) <= 1)  continue ;
                      ans += DP(pos-1 , i , 0 , isend&&i==d) ;
                 }
            }
      }
      if(!isend) dp[pos][pre] = ans ;
      return ans ;
}

LL    Ans(LL x){
      int Len = 0 ;
      while(x){
           bit[Len++] = x % 10  ;
           x /= 10 ;
      }
      return  DP(Len-1 , 10 , 1 ,  1) ;
}


HDU 4389     求x在[L, R], 满足x % 数码和=0  。 的个数

 这个题,直接搞不行。   将题变一下。 变成 数码和为定值d 。那么就好办了。  记录数码和, %d的余数,最后判断余数是否为0 , 数码和是否为d即可。
 而这个1 <= d <= 81 呀。  那么枚举d就ok了。
int    DP(int pos , int prefixsum , int mod ,  int  res , int isend){
       if(pos == -1){
             if(prefixsum == 0 || prefixsum != mod) return 0 ;
             return res == 0 ;
       }
       if(!isend && dp[pos][prefixsum][mod][res] != -1) return dp[pos][prefixsum][mod][res] ;
       int ans = 0 ;
       int d = isend ? bit[pos] : 9 ;
       for(int i = 0 ;  i <= d ; i++)
             ans += DP(pos-1 , prefixsum+i , mod , (res*10+i) % mod , isend&&i==d) ;
       if(!isend)  dp[pos][prefixsum][mod][res] = ans ;
       return ans ;
}

int    Ans(int  x){
       int  Len = 0  , ans = 0 ;
       while(x){
            bit[Len++] = x % 10 ;
            x /= 10 ;
       }
       int d = min(81 , 9*Len) ;
       for(int i = 1 ; i <= d ; i++)
           ans += DP(Len-1 , 0 , i , 0 , 1) ;
       return ans ;
}

Ural 1057 在[L,R]间的自然数总数,这些自然数必须满足条件:和是由K个不同的整数(这些整数等于B的若干次方)组成

将x表示成B进制, 统计在区间 【L, R】1的个数和恰好为K个 的总数 。
LL  DP(int pos , int sum , int b , int isend){
     if(pos == -1) return sum == k ;
     if(sum > k) return 0 ;
     if(!isend && dp[pos][sum][b] != -1) return dp[pos][sum][b] ;
     LL ans = 0 ;
     int t = isend ? min(1 ,bit[pos]) : 1 ;
     for(int i = 0 ; i <= t ; i++)
         ans += DP(pos-1 , sum + (i>0? 1 : 0) , b , isend&&i==bit[pos]) ;//i==bit[pos]
     if(!isend) dp[pos][sum][b] = ans ;
     return  ans ;
}






 


你可能感兴趣的:(数位dp总结)