B-number HDU - 3652 (数位DP)

讲数位DP讲的很好的一篇博客:https://blog.csdn.net/wust_zzwh/article/details/52100392#commentsedit

题目链接:

B-number

 HDU - 3652 

题目大意:给你一个n,然后问你从1~n中有多少个数是能被13整除并且包含13的。

具体思路:dp[i][j][k]表示到第i位的时候,余数为0,并且凑成13的类型为k的时候的状态(0为之前的没有凑齐过13,1为之前只凑齐了1,2位之前凑齐了13)(具体的j和k只是表示当前位数的余数是多少,并且类型为多少;具体算的时候还是算的当前位数之前的总合法的数目)

数位DP求的就是一个前缀和?

每一次dp[i][][] 表示的是当前位之后满足题目条件的个数,每一次dfs枚举的状态只是对于当前位和当前位之前的状态的一个表述。

对于为什么要加一个限制条件,简单的举个例子,我们假设要找[1,210]这个区间满足的数,当百位取0的时候,我们十位当前枚举到了1这个状态,

此时个位是能取0~9的,但是,当我们百位取2的时候,我们十位还是枚举到1这个状态,我们此时个位只能为0,但是如果不加限制条件的话,通过记搜,我们此时的个位状态还是按照0~9取的,也就是说会算多了,这也就是会算多的地方。

AC代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 # define ll long long
 4 # define inf 0x3f3f3f3f
 5 const int maxn = 1100;
 6 int a[maxn];
 7 int dp[maxn][14][3];
 8 int check(int t1,int t2){
 9 if(t1==1&&t2!=1&&t2!=3)return 0;
10 if(t1==0&&t2==1)return 1;
11 if(t1==1&&t2==3)return 2;
12 return t1;
13 }
14 int dfs(int pos,int is_head,int mod,int is_13){
15 if(!pos)return mod==0&&is_13==2;
16 if(!is_head&&dp[pos][mod][is_13]!=-1)return dp[pos][mod][is_13];
17 int fmax = is_head ? a[pos]:9;
18 int ans=0;
19 for(int i=0;i<=fmax;i++){
20 ans+=dfs(pos-1,is_head&&i==fmax,(mod*10+i)%13,check(is_13,i));
21 }
22 if(!is_head)dp[pos][mod][is_13]=ans;
23 return ans;
24 }
25 int cal(int t){
26 int num=0;
27 memset(dp,-1,sizeof(dp));
28 while(t){
29 a[++num]=t%10;
30 t/=10;
31 }
32 return dfs(num,1,0,0);
33 }
34 int main(){
35 int n;
36 while(~scanf("%d",&n)){
37 printf("%d\n",cal(n));
38 }
39 return 0;
40 }

 其实还可以把约束条件放在数组里面,也就是数组多开一维。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 # define ll long long
 4 const int maxn = 2e5  + 100;
 5 const int N  = 100;
 6 int dp[N][14][3][2];
 7 int a[N];
 8 int check(int t1,int t2){
 9 if(t1==1&&t2!=1&&t2!=3)return 0;
10 if(t1==1&&t2==3)return 2;
11 if(t1==1&&t2==1)return 1;
12 if(t1==0&&t2==1)return 1;
13 return t1;
14 }
15 int dfs(int pos,int is_head,int mod,int type){
16 if(!pos)return mod==0&&type==2;
17 if(dp[pos][mod][type][is_head]!=-1)return dp[pos][mod][type][is_head];
18 int fmax = is_head ? a[pos] : 9;
19 int ans=0;
20 for(int i=0;i<=fmax;i++){
21 ans+=dfs(pos-1,is_head&&i==fmax,(mod*10+i)%13,check(type,i));
22 }
23 dp[pos][mod][type][is_head]=ans;
24 return ans;
25 }
26 int cal(int t){
27 memset ( dp , -1 , sizeof(dp));
28 int num=0;
29 while(t){
30 a[++num]=t%10;
31 t/=10;
32 }
33 return dfs(num,1,0,0);
34 }
35 int main(){
36 int n;
37 while(~scanf("%d",&n)){
38 printf("%d\n",cal(n));
39 }
40 return 0;
41 }
View Code

 

posted @ 2019-05-08 22:14  Let_Life_Stop  阅读(228)  评论(0编辑  收藏  举报