CF1036C Classy Numbers(数位DP)

137 阅读1分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。 题目链接:Classy Numbers - 洛谷

题目: Let's call some positive integer classy if its decimal representation contains no more than 33 non-zero digits. For example, numbers 44 , 200000200000 , 1020310203 are classy and numbers 42314231 , 102306102306 , 72774200007277420000 are not.

You are given a segment [L; R][L;R] . Count the number of classy integers xx such that L \le x \le RL≤x≤R .

Each testcase contains several segments, for each of them you are required to solve the problem separately.

定义一个数字是“好数”,当且仅当它的十进制表示下有不超过3个数字1∼9

举个例子:4,200000,102034,200000,10203是“好数”,然而4231,102306,72774200004231,102306,7277420000不是

给定[l,r],问有多少个x使得 l≤x≤r,且x是“好数”

一共有T(1≤T≤10^4)组数据,对于每次的询问,输出一行一个整数表示答案

1<=li<=ri<=10^18

分析:这道题目显然也是一个不需要考虑前导0的题目,因为条件中要求的与0无关,所以我们只需要在数位DP中记录出现了多少个1到9即可,而且我们要在数位DP过程中剪枝,就是当出现1~9的次数大于3时就直接返回0即可,这样会对程序起到优化作用。

还有一点就是因为本题条件是不变的,只是询问有多个,所以我们只需要初始化一次f数组即可,在进行其他组询问时可以沿用之前已经搜索得到的f数组的值,这样也会减少搜索的时间。

下面是代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
typedef long long ll;
const int N=20;
ll f[N][N];//f[i][j]表示遍历到第i位含有j个数字1~9的方案数
ll a[N];
ll dp(int pos,int cnt,int limit)
{
	if(!pos) return cnt<=3;
	if(cnt>3) return 0;
	if(!limit&&f[pos][cnt]!=-1) return f[pos][cnt];
	int up=limit?a[pos]:9;
	ll ans=0;
	for(int i=0;i<=up;i++)
		ans+=dp(pos-1,cnt+(i!=0),limit&&(i==up));
	if(!limit) f[pos][cnt]=ans;
	return ans;
}
ll solve(ll x)
{
	if(!x) return 1;
	int pos=0;
	while(x)
	{
		a[++pos]=x%10;
		x/=10;
	}
	return dp(pos,0,1);
}
int main()
{
	int T;
	cin>>T;
	memset(f,-1,sizeof f);
	ll l,r;
	while(T--)
	{
		scanf("%lld%lld",&l,&r);
		printf("%lld\n",solve(r)-solve(l-1));
	}
	return 0;
}