题目链接: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
题目分析
题意很简单,求出1到
n
n
n内所有能被13整除且包含子串"13" 数的个数。
虽然我这里不要脸地写了一个数位
d
p
dp
dp入门,但是刚接触数位
d
p
dp
dp的时候写这题着实被坑了好久,差点
d
e
b
u
g
debug
debug哭出来。
思路其实很好想。用
d
p
[
i
]
[
j
]
[
m
]
[
0
/
1
]
dp[i][j][m][0/1]
dp[i][j][m][0/1]记录最高位、最高位的数、13除这个数的余数、是否已包含13,状态转移方程也不难
n
=
(
m
−
(
j
∗
1
0
i
)
%
13
+
13
)
%
13
n=(m - (j*10^i) \%13 + 13)\% 13
n=(m−(j∗10i)%13+13)%13
d
p
[
i
]
[
j
]
[
m
]
[
0
]
=
∑
k
=
0
9
d
p
[
i
−
1
]
[
k
]
[
n
]
[
0
]
dp[i][j][m][0]=\sum_{k=0}^{9}dp[i-1][k][n][0]
dp[i][j][m][0]=k=0∑9dp[i−1][k][n][0]
d
p
[
i
]
[
j
]
[
m
]
[
0
]
=
∑
k
=
0
9
d
p
[
i
−
1
]
[
k
]
[
n
]
[
0
]
dp[i][j][m][0]=\sum_{k=0}^{9}dp[i-1][k][n][0]
dp[i][j][m][0]=k=0∑9dp[i−1][k][n][0]
d
p
[
i
]
[
1
]
[
m
]
[
1
]
+
=
d
p
[
i
−
1
]
[
3
]
[
n
]
[
0
]
dp[i][1][m][1]+=dp[i-1][3][n][0]
dp[i][1][m][1]+=dp[i−1][3][n][0]
d
p
[
i
]
[
1
]
[
m
]
[
1
]
−
=
d
p
[
i
−
1
]
[
3
]
[
n
]
[
0
]
dp[i][1][m][1]-=dp[i-1][3][n][0]
dp[i][1][m][1]−=dp[i−1][3][n][0]是不是很简单。。。。大。。大概吧。。。
虽然看着有些复杂,但是理念是很清晰的,当最高位为1时,需要考虑下一位为3,然后从不包含13到包含13的特殊情况,剩下的都可以直接转移。
后面进行计算的时候要注意,有可能输入的数自身包含13,这时候后面的数其实是不需要包含13的,如输入1339时1326其实也是符合的,但是在转移到十位数上时如不进行特殊判断,很容易漏掉。其次就是转移的时候别忘了前面的数也是需要对13求余并决定后面的余数的(废话)。还有一个比较容易忽略的点就是前一位是1,后一位大于3,那么后一位取3时也不需要强制包含13,因为1和3已经凑出来了。另外就是要避免重复计算,这个注意就行。
赠送几组数据:
13
14
12
1313
1325
1326
130
129
140
1000000000
输出
1
1
0
5
5
6
2
1
2
5993844
AC代码:
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
int dp[10][10][13][2];
int tmp[10];
int t[10];
void init()
{
memset(dp, 0, sizeof(dp));
for(int i = 0; i < 10; i++)
dp[0][i][i][0] = 1;
for(int i = 1; i <= 8; i++){
for(int j = 0; j < 10; j++){
for(int m = 0; m < 13; m++){
int n = (m - (j*t[i])%13 + 13) % 13;
for(int k = 0; k < 10; k++){
dp[i][j][m][1] += dp[i-1][k][n][1],
dp[i][j][m][0] += dp[i-1][k][n][0];
}
if(j == 1){
dp[i][j][m][1] += dp[i-1][3][(m - t[i]%13 + 13)%13][0];
dp[i][j][m][0] -= dp[i-1][3][(m - t[i]%13 + 13)%13][0];
}
}
}
}
}
int query(int n)
{
if(n==1000000000) n -= 1; //由于1e9就这一个数,没有单独多算一位的意义,就直接把它降位了
int dex = 0;
while(n){
tmp[dex++] = n % 10;
n /= 10;
}
tmp[dex--] = 0;
int res = 0;
bool flag = false;
int mod = 0;
while(dex >= 0){
if(flag)
for(int i = 0; i < tmp[dex]; i++)
res += dp[dex][i][(13-mod)%13][0];
for(int i = 0; i < tmp[dex]; i++)
res += dp[dex][i][(13-mod)%13][1];
if(!flag && tmp[dex+1] == 1 && tmp[dex] > 3) res += dp[dex][3][(13-mod)%13][0];
mod = (mod + t[dex]*tmp[dex]) %13;
if(tmp[dex] == 3 && tmp[dex+1] == 1) flag = true;
dex--;
}
if(mod == 0 && flag) res++;
return res;
}
int main()
{
t[0] = 1;
for(int i = 1; i < 10; i++)
t[i] = t[i-1] * 10;
init();
int n;
while(scanf("%d", &n)!=EOF)
printf("%d\n", query(n));
}