数位DP 学习笔记

板子题

出处
escription
蒟蒻hzwer NOIP2014惨跪,他依稀记得他的准考证号是37,现在hzwer又将要面临一场比赛,他希望准考证号不出现37(连续),同时他又十分讨厌4,所以也不希望4出现在准考证号中。。。现在他想知道在A和B之间有多少合法的准考证号
Input
包含两个整数,A B
Output
一个整数。
Sample Input
「输入样例一」

1 10

「输入样例二」

25 50

Sample Output
「输出样例一」

9

「输出样例二」

14

「数据规模和约定」
\(20\%\) 的数据,满足 \(1 \le A \le B \le 1000000\)
\(100\%\) 的数据,满足 \(1 \le A \le B \le 2000000000\)

算法解析

因为不要求输出路径,而且不能贪心,那么考虑DP。
但是我们发现DP的状态很难设计,这里就要用到数位DP来解决这个问题了。
数位DP把一个数字拆开成若干位,如下图,把每一位当做一个节点,也就是说,把每一位当做一个独立的节点,然后设计方程式。一般方程状态是位数和当前位的状态。

数位DP一般有两种计算方法,一种是记忆化搜索(递归),另一种是递推。因为一般给出条件的时候不是位数,而是具体的一个数字。
我们发现,如果要用记搜的话,就要从高到低进行扫描;递推则从低到高。

题目解析

\(\def\foo{\operatorname{f}} \foo(x,y,op)\) 表示从高到低第 \(x\) 位,前一位为 \(y\) ,如果 \(op=1\) 则之前的数字取的是最大值,否则不是的答案。
递推式显然。
算法复杂度为 \(\Theta\left(10N\right)\)\(N\) 为位数, \(10\) 就是指总共每位有 \(10\) 个数字。

代码

(此代码防抄袭)

#include<cstdio>
#include<cstring>
#define maxn 39
using namespace std;
#define debug
typedef int Type;
inline Type read(){
	Type sum=0;
	int flag=0;
	char c=getchar();
	while((c<'0'||c>'9')&&c!='-') c=getchar();
	if(c=='-') c=getchar(),flag=1;
	while('0'<=c&&c<='9'){
		sum=(sum<<1)+(sum<<3)+(c^48);
		c=getchar();
	}
	if(flag) return -sum;
	return sum;
}
int from,to;
int tmp[maxn],n;
int dp[maxn][maxn][2];
void swap(int &x,int &y){ x^=y^=x^=y; }
int f(int x,int y,int op){
	if(dp[x][y][op]) return dp[x][y][op];
	if(x==n+1) return dp[x][y][op]=1;
	int cnt=0;
	if(op){
		for(int i=0;i<=tmp[x]-1;i++)
		    if(i!=4&&(y!=3||i!=7))
		        cnt+=f(x+1,i,0);
		if(tmp[x]!=4&&(y!=3||tmp[x]!=7))
		    cnt+=f(x+1,tmp[x],1);
	}
	else{
		for(int i=0;i<=9;i++)
		    if(i!=4&&(y!=3||i!=7))
		        cnt+=f(x+1,i,0);
	}
	return dp[x][y][op]=cnt;
}
int getans(int x){
	n=0;
	while(x){
		tmp[++n]=x%10;
		x/=10;
	}
	memset(dp,0,sizeof(dp));
	for(int i=1;i<=(n>>1);i++)
	    swap(tmp[i],tmp[n-i+1]);
	return f(1,tmp[1],1);
}
int main(){
	freopen("1.in","r",stdin);
	freopen("my.out","w",stdout);
	#ifdef debug
	to=read(); printf("%d",getans(to));
	#else
	from=read(); to=read();
	printf("%d",getans(to)-getans(from-1));
	#endif
	return 0;
}

推荐练习

[ZJOI2010]数字计数
[SCOI2009] windy 数
又可以水很多蓝题和紫题了

posted @ 2021-02-15 22:35  jiangtaizhe001  阅读(41)  评论(0编辑  收藏  举报