B-number HDU - 3652 (数位DP)
讲数位DP讲的很好的一篇博客:https://blog.csdn.net/wust_zzwh/article/details/52100392#commentsedit
题目链接:
B-number
题目大意:给你一个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 }