HDU 3652 - B-number 数位dp入门

本文详细解析HDU3652-B-number题目,介绍如何利用数位DP解决寻找1到n范围内既能被13整除又包含子串'13'的数的问题。分享了AC代码及注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接: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(j10i)%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=09dp[i1][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=09dp[i1][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[i1][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[i1][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));
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值