数位dp【模板 + 老年康复】

学习博客:戳这里

练手题目1:戳这里

题意:求1-n内有多少个数满足各位之和整除该数。

解题思路:数位dp,枚举各位之和。

附ac代码【模板】:

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<stdio.h>
 4 #include<string.h>
 5 #include<string>
 6 using namespace std;
 7 typedef long long ll;
 8 int a[22];
 9 ll dp[20][220][220];//不同题目状态不同
10 int mod;
11 ll dfs(int pos, int state/*state变量*/, int r/*其他记录点,在这里是余数*/, bool limit/*数位上界变量*/)
12 {
13     //递归边界,既然是按位枚举,最低位是0,那么pos==0说明这个数我枚举完了
14     if(pos == 0) return (state == mod && !r);
15     /*这里一般返回1,表示你枚举的这个数是合法的,那么这里就需要你在枚举时必须每一位都要满足题目条件,
16     也就是说当前枚举到pos位,一定要保证前面已经枚举的数位是合法的。
17     不过具体题目不同或者写法不同的话不一定要返回1 */
18     //第二个就是记忆化(在此前可能不同题目还能有一些剪枝)
19     if(dp[pos][state][r] != -1 && !limit) return dp[pos][state][r];
20     /*常规写法都是在没有限制的条件记忆化,这里与下面记录状态是对应*/
21     int up = limit?a[pos]:9;//根据limit判断枚举的上界up;
22     ll ans = 0;
23     //开始计数
24     for(int i = 0; i <= up; ++i)
25     {
26         if(i + state > mod) break;//剪枝
27         ans += dfs(pos - 1, state + i, (r * 10 + i) % mod, limit && i == a[pos]);//最后两个变量传参都是这样写的
28         /*这里还算比较灵活,不过做几个题就觉得这里也是套路了
29         大概就是说,我当前数位枚举的数是i,然后根据题目的约束条件分类讨论
30         去计算不同情况下的个数,还有要根据state变量来保证i的合法性,比如题目
31         要求数位上不能有62连续出现,那么就是state就是要保存前一位pre,然后分类,
32         前一位如果是6那么这意味就不能是2,这里一定要保存枚举的这个数是合法*/
33     }
34     //计算完,记录状态
35     if(!limit) dp[pos][state][r] = ans;
36     /*这里对应上面的记忆化,在一定条件下时记录,保证一致性,当然如果约束条件不需要考虑lead,这里就是lead就完全不用考虑了*/
37     return ans;
38 }
39 ll solve(ll n)
40 {
41     int pos = 0;
42     ll x = n;
43     while(x)//把数位都分解出来
44     {
45         a[++pos] = x % 10;//个人老是喜欢编号为[1,pos],看不惯的就按自己习惯来,反正注意数位边界就行
46         x /= 10;
47     }
48     ll ans = 0;
49     for(int i = 1; i <= 9 * pos; ++i)//枚举模
50     {
51         mod = i;
52         //初始化dp数组为-1
53         memset(dp, -1, sizeof(dp));
54         ans += dfs(pos/*从最高位开始枚举*/, 0, 0/*一系列状态 */, true);//刚开始最高位都是有限制的,显然比最高位还要高的一位视为0嘛
55     }
56     return ans;
57 }
58 int main()
59 {
60     int t;
61     scanf("%d", &t);
62     ll n;
63     for(int cas = 1; cas <= t; ++cas)
64     {
65         scanf("%lld", &n);
66         printf("Case %d: %lld\n", cas, solve(n));
67     }
68 
69 
70 }
View Code

猜你喜欢

转载自www.cnblogs.com/zmin/p/9438429.html