# 数位dp专题

#include <stdio.h>
#include <string.h>
using namespace std;
int a, b, digit[20], dp[20][2];

int dfs(int pos, bool if4, bool limit)
{
if (pos == 0)
return 1;/*如果一个数的数位从前到后遍历一遍没有continue,这个数符合条件，返回1*/
/*这里一般返回1，表示你枚举的这个数是合法的，那么这里就需要你在枚举时必须每一位都要满足题目条件，也就是说当前枚举到pos位，一定要保证前面已经枚举的数位是合法的。不过具体题目不同或者写法不同的话不一定要返回1 */
if (!limit && dp[pos][if4])
return dp[pos][if4];
/*为什么要返回呢？可以画图理解当我们搜到3XXX时，程序运行到1XXX时就已经把3XXX之后的搜索完了，记忆化也是这个用意*/
int cnt = 0;
int maxx = limit ? digit[pos] : 9;
/*求出最高可以枚举到哪个数字*/
for (int i = 0; i <= maxx; i++)
{
if (if4 && i == 9)
continue;
cnt += dfs(pos - 1, i == 4, limit && i == maxx);
/*只有之前有限制现在的达到了上限才能构成限制*/
}
return limit ? cnt : dp[pos][if4] = cnt;
/*如果没有限制,代表搜满了,可以记忆化,否则就不能*/
}

int solve(int x)
{
memset(digit, 0, sizeof(digit));
int pos = 0;
while (x)
{
digit[++pos] = x % 10;//计算每一位的数,放进digit数组;
x /= 10;
}
return dfs(pos, false, true);
}

int main()
{
scanf("%d%d", &a, &b);
printf("%d\n", solve(b) - solve(a - 1));
return 0;
}


HDU 3555 Bomb

Problem Description
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?

Input
The 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.

Output
For each test case, output an integer indicating the final points of the power.

Sample Input
3
1
50
500

Sample Output
0
1
15

Hint
From 1 to 500, the numbers that include the sub-sequence "49" are "49","149","249","349","449","490","491","492","493","494","495","496","497","498","499",so the answer is 15.

#include <stdio.h>
#include <string.h>

long long n, dp[20][2];
int digit[20];

long long dfs(int pos, bool if4, bool limit)
{
if (pos == 0)
return 1;
if (!limit && dp[pos][if4])
return dp[pos][if4];
long long cnt = 0;
int maxx = limit ? digit[pos] : 9;
for (int i = 0; i <= maxx; i++)
{
if (if4 && i == 9)
continue;
cnt += dfs(pos - 1, i == 4, limit && i == maxx);
}
return limit ? cnt : dp[pos][if4] = cnt;
}

long long solve(long long x)
{
memset(digit, 0, sizeof(digit));
int pos = 0;
while (x)
{
digit[++pos] = x % 10;
x /= 10;
}
return dfs(pos, false, true);
}

int main()
{
int t;
scanf("%d", &t);
while (t--)
{
scanf("%lld", &n);
printf("%lld\n", n + 1 - solve(n));
}
return 0;
}


HDU 3652 B-number

Problem Description
A wqb-number, or B-number for short, is a non-negative integer whose decimal form contains the sub- string "13" and can be divided by 13. For example, 130 and 2613 are wqb-numbers, but 143 and 2639 are not. Your task is to calculate how many wqb-numbers from 1 to n for a given integer n.

Input
Process till EOF. In each line, there is one positive integer n(1 <= n <= 1000000000).

Output
Print each answer in a single line.

Sample Input
13
100
200
1000

Sample Output
1
1
2
2

#include <stdio.h>
#include <string.h>

long long n, dp[20][3][15];
/*dp[i][j][k]
i：数位
j：3种操作状况：0：末尾不是1；1：末尾是1；2：含有13
k：余数*/
int digit[20];

long long dfs(int pos, int pre, int mod, bool limit)
{
if (pos <= 0)
return pre == 2 && mod == 0;
if (!limit && dp[pos][pre][mod] != -1)
return dp[pos][pre][mod];
long long cnt = 0;
int maxx = limit ? digit[pos] : 9;
for (int i = 0; i <= maxx; i++)
{
if (pre == 2 || (pre == 1 && i == 3))
cnt += dfs(pos - 1, 2, (mod * 10 + i) % 13, limit && i == maxx);
else if (i == 1)
cnt += dfs(pos - 1, 1, (mod * 10 + i) % 13, limit && i == maxx);
else
cnt += dfs(pos - 1, 0, (mod * 10 + i) % 13, limit && i == maxx);
/*(mod * 10 + i) % 13这一步就是模拟除法过程，看能否整除13*/
/*根据pre判断三种操作状况，如果这一位是1，则把pre标为1，如果pre是2或者上一位是1且这一位是3，则把pre标为2，如果都不是，则把pre标为0*/
/*pre=2表示这个数位里包含了13，pre=1表示这个数位没包含13但是上一位是1，pre=0表示这个数位既不包含13，上一位也不是1*/
}
return limit ? cnt : dp[pos][pre][mod] = cnt;
}

long long solve(long long x)
{
memset(digit, 0, sizeof(digit));
int pos = 0;
while (x)
{
digit[++pos] = x % 10;
x /= 10;
}
return dfs(pos, 0, 0, 1);
}

int main()
{
while (~scanf("%lld", &n))
{
memset(dp, -1, sizeof(dp));
printf("%lld\n", solve(n));
}
return 0;
}

posted @ 2019-05-03 11:21  redleaves  阅读(143)  评论(0编辑  收藏
Live2D