hdu2089&&数圈圈(数位dp)

题目链接:

1,http://acm.hdu.edu.cn/showproblem.php?pid=2089

2,https://www.nowcoder.com/acm/contest/30/D

 

都是判断一个区间中的数字满足什么什么条件的有多少个,暴力的做法直接遍历,一个个数字的判断,然而数位dp很快,几乎在O(n*102),n是数字的位数,是非常快的

数位dp也就是数位之间的dp,如十位与个位,百位与十位。在测试数据组数很多的话,可以预处理,但一般不必要,直接记忆化搜索,要方便得多

数圈圈的AC代码:

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<iostream>
 4 #include<math.h>
 5 using namespace std; 
 6 typedef long long ll;
 7 ll dp[16][10]={0},pos[10],ning[20];
 8 
 9 ll pow1(ll a,ll b)
10 {    ll sum=1;
11     
12     for(ll i=1;i<=b;i++)
13         sum*=a;
14     return sum;
15     
16 }
17 
18 ll dfs(int x) //x+1为位数,x为在数组中的位置 
19 {    ll sum=0;
20     if(x==-1) return dp[1][pos[x+1]];
21     for(int i=0;i<pos[x];i++)  
22         {    
23             sum+=dp[x+1][i];
24         
25         }
26     
27     sum+=dfs(x-1);    
28     if(x>0)        //需特判。。 
29     {
30         if(pos[x]==0||pos[x]==4||pos[x]==6||pos[x]==9)
31             {    ll y=0;
32                 for(int i=x-1;i>=0;i--)
33                     y=y*10+pos[i];
34                 
35                 sum+=y+1;
36             }
37         if(pos[x]==8)
38         {    ll y=0;
39                 for(int i=x-1;i>=0;i--)
40                     y=y*10+pos[i];
41                 
42                 sum+=2*(y+1);
43             
44         }
45     }
46     
47     return sum;    
48 }
49 
50 ll solve(ll x)
51 {    int tot=0;
52     if(x==0) return 0;
53     while(x)
54     {    
55         pos[tot++]=x%10;
56         x=x/10;
57     }
58 
59     return dfs(tot-1)-ning[tot]; //减去前导0的影响        
60 }
61 
62 int main()
63 {    dp[1][0]=1,dp[1][4]=1,dp[1][6]=1,dp[1][8]=2,dp[1][9]=1; //预处理 
64     ning[1]=1;
65     for(int i=2;i<20;i++)
66         ning[i]=ning[i-1]*10+1;
67     for(ll i=2;i<16;i++)
68         {    for(int j=0;j<10;j++)
69                 {    
70                     if(j==0||j==4||j==6||j==9)
71                         dp[i][j]=pow1(10,i-1);
72                     if(j==8) dp[i][j]=2*pow1(10,i-1);        
73                         
74                     for(int k=0;k<10;k++)
75                         {    
76                             dp[i][j]+=dp[i-1][k];
77                         }
78                 }    
79             
80         }
81     int T;
82     scanf("%d",&T);
83     ll l,r;
84     while(T--)
85     {    scanf("%lld%lld",&l,&r);
86         
87         printf("%lld\n",solve(r)-solve(l-1));
88     }    
89     
90     
91     return 0;
92 } 
View Code

当时做了预处理,现在觉得还是直接记忆化搜索更方便,不用麻烦地特判处于上界的情况(如666,6就是每位的上界)

posted @ 2018-01-15 23:49  hzhuan  阅读(186)  评论(0编辑  收藏  举报