数位dp/记忆化搜索


一、引例

#1033 : 交错和
时间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
给定一个数 x,设它十进制展从高位到低位上的数位依次是 a0, a1, ..., an - 1,定义交错和函数:

f(x) = a0 - a1 + a2 - ... + ( - 1)n - 1an - 1

例如:

f(3214567) = 3 - 2 + 1 - 4 + 5 - 6 + 7 = 4

给定 l, r, k,求在 [l, r] 区间中,所有 f(x) = k 的 x 的和,即:

1405402477702.png

输入
输入数据仅一行包含三个整数,l, r, k(0 ≤ l ≤ r ≤ 1018, |k| ≤ 100)。

输出
输出一行一个整数表示结果,考虑到答案可能很大,输出结果模 109 + 7。


提示
对于样例 ,满足条件的数有 110 和 121,所以结果是 231 = 110 + 121。

 1 /*
 2 ************************************交错和***********************************************
 3 ******************************by JA/C++ 2015-1-19****************************************
 4 */
 5 
 6 
 7 #include <cstdio>
 8 #include <cstring>
 9 long long mod = 1000000007;
10 long long base[20];
11 long long l, r, k, bit[20], bt, yy;
12 struct node {
13     long long s, n;//s代表数字和,n代表数字个数
14 };
15 node dp[20][400];//状态转移
16 node dfs(long long pos, long long target, long long limit)//数位dp,基本可以算是模板啦
17 {
18     node t;
19     t.s = t.n = 0;
20     if (pos == 0) {               //处理到最后一位,直接判断返回
21         if (target == 100)
22             t.n = 1;
23         return t;
24     }
25     if ((limit == 0) && (dp[pos][target].n != -1)) return dp[pos][target];
26     long long tail = limit ? bit[pos] : 9;
27     long long sgn = ((yy - pos) % 2) ? (-1) : (1);//确定符号
28     long long head;
29     if (pos == yy)head = 1;
30     else head = 0;//确定搜索的起点和终点
31     for (long long  i= head; i <= tail; i++)
32     {
33         node tmp = dfs(pos - 1, target - i*sgn, (limit == 1) && (i == bit[pos]));
34         if ((tmp.n)>0){
35             t.n += tmp.n;
36             long long q;
37             q = ((((tmp.n%mod)*base[pos]) % mod)*i) % mod;//结果的同余处理
38             t.s += (tmp.s) % mod;
39             t.s %= mod;
40             t.s += q;
41             t.s %= mod;//每一步都要同余
42         }
43     }
44     if (limit == 0) dp[pos][target] = t;
45     return t;
46 }
47 long long cal(long long x, long long y)
48 {
49     long long ans = 0;
50     if (x == -1) return 0;
51     if (x == 0) return 0;
52     bt = 0;
53     while (x)
54     {
55         bt++;
56         bit[bt] = x % 10;
57         x /= 10;
58     }
59     for (yy = 1; yy <= bt; yy++){
60         memset(dp, -1, sizeof dp);
61         ans += dfs(yy, y + 100, yy == bt).s;//对于每个长度为yy的数字进行处理
62         ans = (ans + mod) % mod;
63     }
64     return ans;
65 }
66 int main()
67 {
68     base[1] = 1;
69     for (int i = 2; i <= 19; i++)
70         base[i] = (base[i - 1] * 10) % mod;
71     scanf("%lld%lld%lld", &l, &r, &k);
72     //scanf_s("%lld%lld%lld", &l, &r, &k);
73     {
74         printf("%lld", (cal(r, k) - cal(l - 1, k) + mod) % mod);
75     }
76     return 0;
77 }
View Code

二、思路分析

给定取值范围,要求区间内数的交错和为给定数,需要用到数位dp,记忆搜索以及同余定理

1.记忆化搜索写的时候要将相同交错和的个数,相同交错和的数字和分别进行dp。

2.对于一位数字和两位数字的计算方式并不相同,要分数字的位数进行讨论。

3.由于结果可能比较大,每一步都需要使用同余定理。

三、数位dp模板

 1 const int MAX_DIGITS, MAX_STATUS;
 2 LL f[MAX_DIGITS][MAX_STATUS], bits[MAX_DIGITS];
 3 
 4 LL dfs(int position, int status, bool limit, bool first)
 5 {
 6   if (position == -1)
 7     return s == target_status;
 8   if (!limit && !first && ~f[position][status])
 9     return f[position][status];
10   int u = limit ? bits[position] : MAX_BITS;
11   LL ret = 0;
12   for (int i = 0; i <= u; i++)
13   {
14     ret += dfs(position - 1, next_status(status, i), limit && i == u, first && !i);
15   }
16   return limit || first ? ret : f[pos][status] = ret;
17 }
18 
19 LL calc(LL n)
20 {
21   CLR(f, -1);
22   int len = 0;
23   while (n)
24   {
25     bits[len++] = n % 10;
26     n /= 10;
27   }
28   return dfs(len - 1, 0, true, true);
29 }
30 
31 int main()
32 {
33   //freopen("0.txt", "r", stdin);
34   LL a, b;
35   while (cin >> a >> b)
36     cout << calc(b) - calc(a - 1) << endl;
37   return 0;
38 }

四、记忆化搜索

记忆化搜索=搜索的形式+动态规划的思想

参考文献

1.算法合集之《浅谈数位类统计问题》——刘聪

2.推酷《数位dp模板

原文地址:https://www.cnblogs.com/joeaaron007/p/4234229.html