数位dp
Posted cgjh
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数位dp相关的知识,希望对你有一定的参考价值。
D - Bomb HDU - 3555
The counter-terrorists found a time bomb in the dust.
But this time the terrorists improve on the time bomb.
The number sequence of the time bomb counts from 1 to N.
If the current number sequence includes the sub-sequence "49", the power of the blast would add one point.
Now the counter-terrorist knows the number N.
They want to know the final points of the power. Can you help them?
InputThe first line of input consists of an integer T (1 <= T <= 10000), indicating the number of test cases. For each test case, there will be an integer N (1 <= N <= 2^63-1) as the description.
The input terminates by end of file marker.
OutputFor each test case, output an integer indicating the final points of the power.
Sample Input
3 1 50 500
Sample Output
0 1 15
题意: 要求一段区间内含49的数的个数.
分析: 考虑一下搜索,我们有dfs(int deep, bool flag, bool Y, bool lmt)
deep表示当前数的位数,flag表示上一个数位是不是为4, Y表示数字含有49
所以我们有,当flag==4的时候,下一个数位是9的时候Y应该为true,并且会影响之后的搜索.
同时我们的记忆数组也可以定义出来了: dp[deep][flag][Y]
#include <cstdio> #include <cstring> using namespace std; typedef long long ll; int digit[64]; ll dp[64][2][2]; ll dfs(int deep, bool Y, bool flag, bool lmt) { if (!deep) return Y; if (!lmt && dp[deep][Y][flag]>=0) return dp[deep][Y][flag]; int i, up = lmt ? digit[deep] : 9; ll res = 0; for (i=0; i<=up; ++i) res += dfs(deep-1, Y || (flag && i==9), i==4, lmt && i==up); return lmt ? res : dp[deep][Y][flag] = res; } ll cal(ll n) { int k = 1; while (n) { digit[k++] = n % 10; n /= 10; } return dfs(k-1, false, false, true); } int main() { int t; ll v; scanf("%d", &t); memset(dp, -1, sizeof dp); while (t--) { scanf("%I64d", &v); printf("%I64d ", cal(v)); } }
C - 不要62 HDU - 2089
杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer)。
杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,
不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍,
更安全地服务大众。
不吉利的数字为所有含有4或62的号码。例如:
62315 73418 88914
都属于不吉利号码。但是,61152虽然含有6和2,但不是62连号,所以不属于不吉利数字之列。
你的任务是,对于每次给出的一个牌照区间号,推断出交管局今次又要实际上给多少辆新的士车上牌照了。
Input输入的都是整数对n、m(0<n≤m<1000000),如果遇到都是0的整数对,则输入结束。
Output对于每个整数对,输出一个不含有不吉利数字的统计个数,该数值占一行位置。
Sample Input
1 100 0 0
Sample Output
80
题意: 要求一段区间不含4和62的数的个数.
分析: 考虑一下搜索,我们有dfs(int deep, bool flag, bool lmt)
deep表示当前数的位数,flag表示上一个数位是不是为6,如果是下一个数是2就不进行搜索
如果数位是4也不进行搜索
同时我们的记忆数组也可以定义出来了: dp[deep][flag]
#include <cstdio> #include <cstring> using namespace std; int digit[10]; int dp[11][2]; inline int dfs(int deep, bool flag, bool lmt) { if (!deep) return 1; if (!lmt && dp[deep][flag]>=0) return dp[deep][flag]; int up = lmt ? digit[deep] : 9; int i, cnt = 0; for (i=0; i<=up; ++i) { if (i==4) continue; if (flag && i==2) continue; cnt += dfs(deep-1, i==6, lmt && i==up); } return lmt ? cnt : dp[deep][flag]=cnt; } int cal(int num) { int i, k = 1; while (num) { digit[k++] = num % 10; num /= 10; } return dfs(k-1, false, true); } int main() { int a, b; memset(dp, -1, sizeof(dp)); while (scanf("%d%d", &a, &b) && (a || b)) printf("%d ", cal(b)-cal(a-1)); return 0; }
A - Beautiful numbers CodeForces - 55D
Volodya is an odd boy and his taste is strange as well.
It seems to him that a positive integer number is beautiful if and only if it is divisible by each of its nonzero digits.
We will not argue with this and just count the quantity of beautiful numbers in given ranges.
Input
The first line of the input contains the number of cases t (1?≤?t?≤?10). Each of the next t lines contains two natural numbers li and ri (1?≤?li?≤?ri?≤?9?·1018).
Please, do not use %lld specificator to read or write 64-bit integers in C++. It is preffered to use cin (also you may use %I64d).
Output
Output should contain t numbers — answers to the queries, one number per line — quantities of beautiful numbers in given intervals (from li to ri, inclusively).
1
1 9
9
1
12 15
2
题意:
如果一个数可以被它每一个数位整除的话,那么这个数就称为一个美数.
现在要我们求区间有多个美数.
分析: 当一个数是美数时,那么他一定可以被它的所有数位的最小公倍数整除.
所以我们可以初步定义dfs(int deep, int val, int lcm, bool lmt)
其中val是这个数,lcm是它所有数位的最小公倍数.
同时,我们需要一个数组来记录我们搜索过程中的状态:
dp[deep][val][lcm] val和lcm的定义就是dfs中的val和lcm.
然后我们发现,如果val和lcm太大了,我们是开不下的. 所以我们需要减小val和lcm.
我们发现,对于一个大于2520(1,2,3,4,5,6,7,8,9的最小公倍数)的数,
我们可以直接在计算的过程中对2520取模,所以val的范围是[0,2520)
但lcm的范围也是[0,2520),所以我们还需要缩小val或者lcm.
我们发现1,2,3,4,5,6,7,8,9的最小公倍数最多只有48个,所以我们类似hash弄一下,
所以lcm这一维就只有48了.
所以我们的数组开: dp[20][2520][48]就行了.
#include <cstdio> #include <cstring> using namespace std; typedef long long ll; int digit[20]; ll dp[20][2520][48]; int _h[2521]; inline int gcd(int a, int b) { return b ? gcd(b, a%b) : a; } inline int lcm(int a, int b) { return a / gcd(a, b) * b; } ll dfs(int deep, int val, int lc, bool lmt) { if (!deep) return val % lc == 0; if (!lmt && dp[deep][val][_h[lc]] >= 0) return dp[deep][val][_h[lc]]; int i, tmp; ll cnt = 0; int up = lmt ? digit[deep] : 9; for (i=0; i <= up; ++i) { if (i) tmp = lcm(lc, i); else tmp = lc; cnt += dfs(deep-1, (val * 10 + i) % 2520, tmp, lmt && i==up); } return lmt ? cnt : dp[deep][val][_h[lc]] = cnt; } ll cal(ll n) { int k = 1; while (n) { digit[k++] = n % 10; n /= 10; } return dfs(k-1, 0, 1, true); } int main() { int t; int i, j; j = 0; for (i=1; i<=2520; ++i) if (2520 % i == 0) _h[i] = j++; memset(dp, -1, sizeof(dp)); scanf("%d", &t); ll left, right; while (t--) { scanf("%I64d%I64d", &left, &right); printf("%I64d ", cal(right) - cal(left-1)); } return 0; }
以上是关于数位dp的主要内容,如果未能解决你的问题,请参考以下文章