数位DP笔记

文章提供了一系列编程竞赛题目,涉及数位操作和递归统计的算法,包括不包含特定数位的数、不包含特定两位数的数、与特定数字无关的数、数码计数以及满足特定条件的整数(如windy数和同类分布)的计数问题。解决方案主要利用动态规划和递归方法进行计算。
摘要由CSDN通过智能技术生成

A. 不包含 x 的数

时间限制:1000 ms内存限制:256 MB类型:传统评测:文本比较上传者: sysulby

提交提交记录

题目描述

统计  范围内,不包含数字  x的数有多少个。

输入格式

输入只有一行三个整数l , r,  x。

输出格式

输出一行,一个整数sum,表示满足条件的数的个数。

样例
样例输入复制
1 100 1
样例输出复制
80

AC代码

#include<bits/stdc++.h>
using namespace std;
int x;
long long dgt[20],n,dp[20][2];
long long dfs(int i,int o){
	if(dp[i][o] != -1)return dp[i][o];
	long long ret = 0;
	if(i == 0)return 1;
	int k = (o ? dgt[i]: 9);
	for(int d = 0;d <= k;d++){
		if(d == x)continue;
		ret += dfs(i-1,o && d == k);
	}
	dp[i][o] = ret;
	return ret;
}
long long calc(long long num){
	n = 0;
	while(num){
		n++;
		dgt[n] = num % 10;
		num = num/10;
	}
	memset(dp,-1,sizeof(dp));
	return dfs(n,1);
}
int main(){

    long long l,r;
    cin>>l>>r>>x;
    cout<<calc(r)-calc(l-1);
} 

 B. 不包含 xx 的数

限制:256 MB类型:传统评测:文本比较上传者: sysulby

提交提交记录

题目描述

统计  范围内,不包含两位数  的数有多少个。

输入格式

输入只有一行三个整数 , , 。

输出格式

输出一行,一个整数,表示满足条件的数的个数。

样例
样例输入复制
1 1000 10
样例输出复制
979

核心思路

在dfs前记录上一位是多少。

如果是目标数则continue;

AC代码

#include<bits/stdc++.h>
using namespace std;
int x;
long long dgt[20],n,dp[20][10][2];
long long dfs(int i,int j,int o){
	if(dp[i][j][o] != -1)return dp[i][j][o];
	long long ret = 0;
	if(i == 0)return 1;
	int k = (o ? dgt[i]: 9);
	for(int d = 0;d <= k;d++){
		if(j*10+d == x)continue;
		ret += dfs(i-1,d,o && d == k);
	}
	dp[i][j][o] = ret;
	return ret;
}
long long calc(long long num){
	n = 0;
	while(num){
		n++;
		dgt[n] = num % 10;
		num = num/10;
	}
	memset(dp,-1,sizeof(dp));
	return dfs(n,0,1);
}
int main(){

    long long l,r;
    cin>>l>>r>>x;
    cout<<calc(r)-calc(l-1);
} 

C. 与 x 无关的数

时间限制:1000 ms内存限制:256 MB类型:传统评测:文本比较上传者: sysulby

提交提交记录

题目描述

统计  范围内,有多少个数与数字  无关。

即数位中既不包含 ,这个数本身也不是  的倍数。

输入格式

输入只有一行三个整数 , , 。

输出格式

输出一行,一个整数,表示满足条件的数的个数。

样例
样例输入复制
1 100 7
样例输出复制
70

核心思路

定义  j 表示,余数,和前面状压博客上的一道题差不多,只要余数不为0就好,不需要知道前面怎么排列。

AC代码

#include<bits/stdc++.h>
using namespace std;
int x;
long long dgt[20],n,dp[20][10][2];
long long dfs(int i,int j,int o){//求第i位状态为o的可能 
	if(dp[i][j][o] != -1)return dp[i][j][o];
	long long ret = 0;
	if(i == 0)return j != 0;
	int k = (o ? dgt[i]: 9);
	for(int d = 0;d <= k;d++){
		if(d == x)continue;
		ret += dfs(i-1,(j*10+d)% x,o && d == k);
	}
	dp[i][j][o] = ret;
	return ret;
}
long long calc(long long num){
	n = 0;
	while(num){
		n++;
		dgt[n] = num % 10;
		num = num/10;
	}
	memset(dp,-1,sizeof(dp));
	return dfs(n,0,1);
}
int main(){

    long long l,r;
    cin>>l>>r>>x;
    cout<<calc(r)-calc(l-1);
} 

D. 数字计数

时间限制:1000 ms内存限制:512 MB类型:传统评测:文本比较上传者: sysulby

提交提交记录

题目描述

原题来自:ZJOI 2010

给定两个正整数  和 ,求在  中的所有整数中,每个数码 (digit) 各出现了多少次。

输入格式

仅包含一行两个整数 ,含义如上所述。

输出格式

包含一行  个整数,分别表示  在  中出现了多少次。

样例
样例输入复制
1 99
样例输出复制
9 20 20 20 20 20 20 20 20 20

AC代码

#include<bits/stdc++.h>
using namespace std;
int x;
long long dgt[20],n,dp[20][2][2];
long long dfs(int i,int o,int z){
	if(dp[i][o][z] != -1)return dp[i][o][z];
	long long ret = 0;
	if(i == 0)return 0;
	int k = (o ? dgt[i]: 9);
	for(int d = 0;d <= k;d++){
	    ret+= dfs(i-1,o&& d == k,z && d == 0);
	    if(d == x){
	    	if(z&&d == 0)continue;
	    	if(o && d == k){
	    		long long tmp = 0;
	    		for(int j = i-1;j >= 1;j--)tmp = tmp*10+dgt[j];
	    		ret += tmp+1;
			}
			else{
				long long tmp = 1;
				for(int j = i-1;j >= 1;j--)tmp = tmp*10;
				ret += tmp;
			}
		}
	}
	dp[i][o][z] = ret;
	return ret;
}
long long calc(long long num){
	n = 0;
	while(num){
		n++;
		dgt[n] = num % 10;
		num = num/10;
	}
	memset(dp,-1,sizeof(dp));
	return dfs(n,1,1);
}
int main(){

    long long l,r;
    cin>>l>>r;
    for(;x <= 9;x++)cout<<calc(r)-calc(l-1)<<" ";
} 

E. windy数

时间限制:1000 ms内存限制:512 MB类型:传统评测:文本比较上传者: sysulby

提交提交记录

题目描述

原题来自:SCOI 2009

windy定义了一种windy数。不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。 windy想知道,在A和B之间,包括A和B,总共有多少个windy数?

输入格式

包含两个整数,A B。

输出格式

一个整数

样例

input1复制

1 10

input2复制

25 50

output1复制

9

output2复制

20

核心思路

记录上一位是多少,并判断是否为前导0。

若这位选择的是小于2,则continue

AC代码

#include<bits/stdc++.h>
using namespace std;
int x;
long long dgt[20],n,dp[20][2][2][10];
long long dfs(int i,int o,int z,int j){
	if(dp[i][o][z][j] != -1)return dp[i][o][z][j];
	long long ret = 0;
	if(i == 0)return 1;
	int k = (o ? dgt[i]: 9);
	for(int d = 0;d <= k;d++){
		if(z == 0 &&abs(d-j) < 2)continue;
	    ret+= dfs(i-1,o&& d == k,z && d == 0,d);
	    
	}
	dp[i][o][z][j] = ret;
	return ret;
}
long long calc(long long num){
	n = 0;
	while(num){
		n++;
		dgt[n] = num % 10;
		num = num/10;
	}
	memset(dp,-1,sizeof(dp));
	return dfs(n,1,1,0);
}
int main(){

    long long l,r;
    cin>>l>>r;
    cout<<calc(r)-calc(l-1);
} 

F. 同类分布

时间限制:3000 ms内存限制:256 MB类型:传统评测:文本比较上传者: sysulby

提交提交记录

题目描述

原题来自:AHOI 2009

给出两个数 ,求出  [a,b]中各位数字之和能整除原数的数的个数。

输入格式

一行,两个整数  a和 b。

输出格式

一个整数sum,表示答案。

样例
样例输入复制
10 19
样例输出复制
3

核心思路

提前枚举模数,若数位和与模数一致则是正确的数。

AC代码

#include<bits/stdc++.h>
using namespace std;
int x,T;
long long dgt[20],n,dp[20][2][200][200];
long long dfs(int i,int o,int sum,int pd){
	if(dp[i][o][sum][pd] != -1)return dp[i][o][sum][pd];
	long long ret = 0;
	if(i == 0&&sum==T&&pd == 0)return 1;
	if(i == 0)return 0;
	int k = (o ? dgt[i]: 9);
	for(int d = 0;d <= k;d++){
	
		ret += dfs(i-1,o && d == k,sum+d,(pd*10+d)%T);
	}
	dp[i][o][sum][pd] = ret;
	return ret;
}
long long calc(long long num){
	n = 0;
	long long ans = 0;
	while(num){
		n++;
		dgt[n] = num % 10;
		num = num/10;
	}
	for(T = 1;T <= n*9;T++){
		memset(dp,-1,sizeof(dp));
		ans += dfs(n,1,0,0);
	}
	
	return ans;
}
int main(){

    long long l,r;
    cin>>l>>r;
    cout<<calc(r)-calc(l-1);
} 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值