关于数位dp

本来是8个月前答应写的,咕咕了240天,汗

数位dp

定义:from-OI Wiki

image

说点人话:

当一道关于特定数字计数的问题出现,值域范围到达\(10^{18}\),可以通过枚举每个数位上的数, 递推/搜索 出答案

小思想:关于统计区间\([L,R]\)之间数的数量

前缀和:\(sum[L,R]=sum[1,R]-sum[1,L-1]\)

具体例题

1. P2602 [ZJOI2010] 数字计数

可以发现,如果直接枚举数字,会炸掉,联想一下会枚举的数字

  • 11111111

  • ......

  • 21111111

  • ''''''

  • 31111111

  • ......

发现重合部分好多,可不可以避免重合呢?

直接告诉你 : 爆搜+剪枝

(比如)搜到亿位时,发现后面的数字出现次数相当,直接返回搜过的值

给出基本代码并讲解
int dfs(int pos,bool limit,int sum,bool lead0,int d){
	int res=0;
	if(!pos) return sum;
	if(!limit&&dp[pos][sum]!=-1) return dp[pos][sum];
	int up=limit?a[pos]:9;
	for(int i=0;i<=up;i++) res+=dfs(pos-1,limit&&i==up,sum+((i||lead0)&&(i==d)),lead0||i,d);
	if(!limit&&lead0) dp[pos][sum]=res; 
	return res;
}

从高到低,一位一位枚举填写的数字

  • pos : 当前填到第几位

  • limit(0/1) : 当前这填到的位数有没有限制(比如我要求\([1,92]\)之间的数,个位就不能填5)

  • sum : 数字$ d $ 的出现次数

  • lead0(0/1) : 有没有前导零(肯定不能统计 0000000123 中有7个0出现嘛)

  • d : 统计的数字是那个

额,逐行解释叭(注释代码)

int dfs(int pos,bool limit,int sum,bool lead0,int d){
	int res=0;//统计答案,为数字记忆化搜索做准备 
	if(!pos) return sum;//搜到最后一位,结束搜索
	//填数没有限制,以前搜索过后继状态,记忆化 
	if(!limit&&dp[pos][sum]!=-1) return dp[pos][sum];
	int up=limit?a[pos]:9;//有限制时最大只为该数位上数 
	for(int i=0;i<=up;i++) res+=dfs(pos-1,limit&&i==up,sum+((i||lead0)&&(i==d)),lead0||i,d);
	//后文解释
	if(!limit&&lead0) dp[pos][sum]=res; //没有特殊限制 作记忆化 
	return res;//返回答案 
}
res+=dfs(pos-1,limit&&i==up,sum+((i||lead0)&&(i==d)),lead0||i,d);
  • $ pos-1 $ : 搜索下一位

  • $ limit $ && $ i==up $ : 该位置有限制且枚举填数最大->下一位有限制

  • \(sum\)+\(((i||lead0)\)&&\((i==d))\):

\(sum\)指原有搜索结果,当没有前导0或i不为0时,i又等与统计的d,才\(sum+1\)

  • \(lead0||i\) : 没有前导0或i不为0

配上拆数函数,也就是数组a的来历

int work(int x,int w){//拆数 
	memset(dp,-1,sizeof(dp));//每次拆数都有统计吖,要清空
	lint len=0;
	while(x){
		a[++len]=x%10;
		x=x/10;
	}
	return dfs(len,1,0,0,w);
}

2. HDU 2089 不要 62

记录一个递归参数 from(0/1):表示上一位是不是6,如果是,该位不能填2

而不选4直接在dfs中判断掉

3. P13085 [SCOI2009] windy 数

一样的,记录上一位数字(int),每次往下搜时判断一下即可

4. P4317 花神的数论题

改一下dp设定,\(dp[pos][cnt]\)表示在\([pos+1,len]\)中已经
已经填了\(cnt\)个1,\([1,pos]\)任意填,合法方案的乘积

posted @ 2025-07-15 19:33  rerecloud  阅读(20)  评论(0)    收藏  举报