HDU 3652 3555 数位dp

本文探讨了如何使用动态规划解决特定计数问题,即计算在[1,n]区间内既包含数字13又能被13整除的数字数量。通过记忆化搜索等技术优化递归过程,有效地解决了问题。

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

/*
    求[1,n]内有多少个数字,该数字有13,且能被13整除   n<=10^9
    
    x % 13 = 0
    (pre*10^pos + next) % 13 = 0   pre是之前确定的部分
    需要的参数为pre , pos , 状态have
    have记录pre拥有"13",pos+1位为"1",没有"13"   分别用have = 2,1,0表示
    
    然后记忆化搜索

*/

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int dp[10][13][3];
int digit[10];

int dfs(int pos, int pre, int have, bool doing) {
	if(pos == -1) 
		return have == 2 && pre == 0;
	if(!doing && dp[pos][pre][have] != -1) 
		return dp[pos][pre][have];
	int ans = 0;
	int end = doing ? digit[pos] : 9;
	for(int i = 0; i <= end; i++) {
		int npre = (pre*10 + i) % 13;
		int nhave = have;
		if(have == 0 && i == 1)
			nhave = 1;
		else if(have == 1 && i!= 1)
			nhave = 0;
		if(have == 1 && i == 3)
			nhave = 2;
		ans += dfs(pos-1, npre, nhave, doing && i == end);
	}
	if(!doing)
		dp[pos][pre][have] = ans;
	return ans;
}

int cal(int x) {
	int pos = 0;
	while(x) {
		digit[pos++] = x % 10;
		x /= 10;
	}
	return dfs(pos-1, 0, 0, 1);
}

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

 

 

3555 是3652的简单版本。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;

long long dp[20][3];
int digit[20];

long long dfs(int pos, int have, bool doing) {
	if(pos == -1) 
		return have == 2;
	if(!doing && dp[pos][have] != -1) 
		return dp[pos][have];
	long long ans = 0;
	int end = doing ? digit[pos] : 9;
	for(int i = 0; i <= end; i++) {
		int nhave = have;
		if(have == 0 && i == 4)
			nhave = 1;
		else if(have == 1 && i!= 4)
			nhave = 0;
		if(have == 1 && i == 9)
			nhave = 2;
		ans += dfs(pos-1, nhave, doing && i == end);
	}
	if(!doing)
		dp[pos][have] = ans;
	return ans;
}

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

int main() {
	int t;
	scanf("%d", &t);
	while(t--) {
		long long n;
		cin >> n;
		memset(dp, -1, sizeof(dp));
		cout << cal(n) << endl;
	}
	return 0;
}



 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值