HDU 3652 B-number (数位DP)
Description
统计区间\([1,n]\)中是\(13\)的倍数且数字中含有“13”的数的个数。
Input
多组用例,处理到文件尾。每组用例给出一个整数\(n\)。\(1 \leqslant n \leqslant 10^9\)
Output
对于每组用例输出一行,表示区间中满足条件的数的数量。
Sample Input
13
100
200
1000
Sample Output
1
1
2
2
Solution
数位DP。要满足的条件有两个:1. 是\(13\)的倍数。2. 含有“13”。
\(dp[pos][r][s][f]\),\(pos\)表示统计的是第\(pos\)到第\(0\)位,\(r\)表示前面的数位对当前产生的余数,\(s\)表示上一位是否为\(1\),\(f\)表示处理到目前为止是否已经有了\(13\)。
如果前面的位置产生的余数是\(r\),当前\(pos\)位的数字是\(i\),那么传给\(pos-1\)位的余数就是\((r+i) \times 10 \mod 13\)。
如果上一位是\(1\),也就是\(s\)为\(true\),且当前位为\(3\)时,说明出现了\(13\),\(f\)标记为\(true\)。
当所有的位置都处理完毕,当且仅当余数为\(0\)且\(f\)为\(true\)时,说明产生了一个合法的数。
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 15;
int dp[N][15][2][2], a[N];
int dfs(int pos, int r, bool s, bool f, bool fp)
{
if (pos < 0) return r == 0 && f;
if (!fp && dp[pos][r][s][f] != -1) return dp[pos][r][s][f];
int ans = 0, fpmax = fp ? a[pos] : 9;
for (int i = 0; i <= fpmax; i++)
ans += dfs(pos - 1, (r + i) * 10 % 13, i == 1, f || s && i == 3, fp && i == fpmax);
if (!fp) dp[pos][r][s][f] = ans;
return ans;
}
int calc(int n)
{
int len = 0;
while (n) a[len++] = n % 10, n /= 10;
return dfs(len - 1, false, false, 0, true);
}
int main()
{
int n;
memset(dp, -1, sizeof(dp));
while (~scanf("%d", &n))
{
printf("%d\n", calc(n));
}
return 0;
}