数位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 }

二、思路分析

给定取值范围,要求区间内数的交错和为给定数,需要用到数位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模板

时间: 2024-11-08 04:30:36

数位dp/记忆化搜索的相关文章

[hihocoder 1033]交错和 数位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(0?≤?l?≤?r?≤?1018,?|k|

HDU 2089 不要62(数位dp&amp;记忆化搜索)

题目链接:[kuangbin带你飞]专题十五 数位DP C - 不要62 题意 杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer). 杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍,更安全地服务大众. 不吉利的数字为所有含有4或62的号码.例如: 62315 73418 88914 都属于不吉利号码.但是,61152虽然含有6和2,但不是62连号,所以不属于不吉利数字之列. 你的任务是,对于每次给

HDU 3652 B-number(数位dp&amp;记忆化搜索)

题目链接:[kuangbin带你飞]专题十五 数位DP G - B-number 题意 求1-n的范围里含有13且能被13整除的数字的个数. 思路 首先,了解这样一个式子:a%m == ((b%m)*c+d)%m; 式子的正确是显然的,就不证明了. 那么判断数是否可以被13整除就可以分解为一位一位进行处理. 当然,我们也只需要储存取余后的值. dfs(len, num, mod, flag) mod记录数字对13取余后的值 len表示当前位数 num==0 不含13且上一位不为1 pre==1

hdu 5787 数位dp,记忆化搜索

题意:求区间[l,r]内有多少个数符合,这个数的任意的相邻k位数(digits),这k个数都两两不相等 l,r范围是1~1e18,k是2~5 思路:数位DP,因为K<=5,我们最多需要保存下来当前位的前4位就足够了.因为dp[pos][p1][p2][p3][p4]表示,现在枚举取第pos位,pos位之前的四位分别为p1,p2,p3,p4,p4是pos的上一位.那么p1~p4的范围就是0~9,但是因为总位数小于当前数字的位数的数也要进行枚举,需要一个数字来区分它是前导0还是在中间时为0,令p =

bzoj1833: [ZJOI2010]count 数字计数(数位DP+记忆化搜索)

1833: [ZJOI2010]count 数字计数 题目:传送门 题解: 今天是躲不开各种恶心DP了??? %爆靖大佬啊!!! 据说是数位DP裸题...emmm学吧学吧 感觉记忆化搜索特别强: 定义f[i][j][k]表示若前i个位置有k个j的此时的全局方案数,然后就可以记忆化搜索了(具体看代码吧) 代码: 1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cmath>

【每日dp】 Gym - 101889E Enigma 数位dp 记忆化搜索

题意:给你一个长度为1000的串以及一个数n 让你将串中的'?'填上数字 使得该串是n的倍数而且最小(没有前导零) 题解:dp,令dp[len][mod]为是否出现过 填到第len位,余数为mod 的状态(dp=0 or 1) 用记忆化搜索来实现,dfs返回1或0. 如果搜到最后一位并且余数为0,返回1. 如果搜到已经更新过的dp状态,直接返回0. 将mod作为全局变量,不断更新mod. 对于每一位 的'? ' 暴力枚举0~9 #include<stdio.h> #include<std

luogu P2657 [SCOI2009]windy数 数位dp 记忆化搜索

题目链接 luogu P2657 [SCOI2009]windy数 题解 我有了一种所有数位dp都能用记忆话搜索水的错觉 代码 #include<cstdio> #include<algorithm> inline int read() { int x = 0,f = 1; char c = getchar(); while(c < '0' || c > '9') c = getchar(); while(c <= '9' && c >= '

hdu3652 数位dp记忆化搜索

从未见过的船新版本数位dp,,省去了预处理过程,直接进行计算 #include<bits/stdc++.h> using namespace std; #define ll long long int dp[20][20][5],n,len,bit[20]; //dp[i][j][k]:到i位数,前面模13=j,前面是1|不是1|有13的状态已经确定,后面的数的个数 //mod:前面%13的余数,flag=0|1|2:pos+1位不是1|是1|有13出现过了,lim:数的限制 int dfs(

11782 - Optimal Cut(树形DP+记忆化搜索)

题目链接:11782 - Optimal Cut 题意:按前序遍历给定一棵满二叉树,现在有k次,可以选k个节点,获得他们的权值,有两个条件: 1.一个节点被选了,他的子节点就不能选了. 2.最终选完后,根到所有叶子的路径上,都要有一个被选的节点. 思路:树形dp,dp[u][k]代表在结点u,可以选k个节点,那么就分两种情况 选u节点,dp[u][k] = node[u]; 选子节点之和,那么就把k次分配给左右孩子,dp[u][k] = max(dp[u][k], dp[u][i], dp[u]