数位DP

Posted shirlybaby

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数位DP相关的知识,希望对你有一定的参考价值。

讲解

https://blog.csdn.net/brazy/article/details/77427699

https://blog.csdn.net/wust_zzwh/article/details/52100392  

数位dp是一种计数用的dp,一般就是要统计一个区间[le,ri]内满足一些条件数的个数。所谓数位dp,字面意思就是在数位上进行dp。

数位的含义:一个数有个位、十位、百位、千位......数的每一位就是数位啦!

之所以要引入数位的概念完全就是为了dp。数位dp的实质就是换一种暴力枚举的方式,使得新的枚举方式满足dp的性质,然后记忆化就可以了

这些问题的特征是给定的区间特别大,不能一个个暴力的解决,必须用O(logN)的方法才行

模板

typedef long long ll;
int a[20];
ll dp[20][state];//不同题目状态不同
ll dfs(int pos,/*state变量*/,bool lead/*前导零*/,bool limit/*数位上界变量*/)//不是每个题都要判断前导零
{
    //递归边界,既然是按位枚举,最低位是0,那么pos==-1说明这个数我枚举完了
    if(pos==-1) return 1;/*这里一般返回1,表示你枚举的这个数是合法的,那么这里就需要你在枚举时必须每一位都要满足题目条件,也就是说当前枚举到pos位,一定要保证前面已经枚举的数位是合法的。不过具体题目不同或者写法不同的话不一定要返回1 */
    //第二个就是记忆化(在此前可能不同题目还能有一些剪枝)
    if(!limit && !lead && dp[pos][state]!=-1) return dp[pos][state];
    /*常规写法都是在没有限制的条件记忆化,这里与下面记录状态是对应,具体为什么是有条件的记忆化后面会讲*/
    int up=limit?a[pos]:9;//根据limit判断枚举的上界up;这个的例子前面用213讲过了
    ll ans=0;
    //开始计数
    for(int i=0;i<=up;i++)//枚举,然后把不同情况的个数加到ans就可以了
    {
        if() ...
        else if()...
        ans+=dfs(pos-1,/*状态转移*/,lead && i==0,limit && i==a[pos]) //最后两个变量传参都是这样写的
        /*这里还算比较灵活,不过做几个题就觉得这里也是套路了
        大概就是说,我当前数位枚举的数是i,然后根据题目的约束条件分类讨论
        去计算不同情况下的个数,还有要根据state变量来保证i的合法性,比如题目
        要求数位上不能有62连续出现,那么就是state就是要保存前一位pre,然后分类,
        前一位如果是6那么这意味就不能是2,这里一定要保存枚举的这个数是合法*/
    }
    //计算完,记录状态
    if(!limit && !lead) dp[pos][state]=ans;
    /*这里对应上面的记忆化,在一定条件下时记录,保证一致性,当然如果约束条件不需要考虑lead,这里就是lead就完全不用考虑了*/
    return ans;
}
ll solve(ll x)
{
    int pos=0;
    while(x)//把数位都分解出来
    {
        a[pos++]=x%10;//个人老是喜欢编号为[0,pos),看不惯的就按自己习惯来,反正注意数位边界就行
        x/=10;
    }
    return dfs(pos-1/*从最高位开始枚举*/,/*一系列状态 */,true,true);//刚开始最高位都是有限制并且有前导零的,显然比最高位还要高的一位视为0嘛
}
int main()
{
    ll le,ri;
    while(~scanf("%lld%lld",&le,&ri))
    {
        //初始化dp数组为-1,这里还有更加优美的优化,后面讲
        printf("%lld
",solve(ri)-solve(le-1));
    }
}

单纯的不要4,两种写法,递推、记忆化搜索(基本也是这两种方法)

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1010;
const int INF=0x3fffffff;
typedef long long LL;
//两种方法 ,递推和记忆化搜索
//单纯的不要4

//递推
int dp[20][10]; //表示第i位数,第1个数字是j时符合条件的数字数量
int a[20];
void inti(){   //初始化先 
	dp[0][0]=1;
	for(int i=1;i<=12;i++){
		for(int j=0;j<10;j++){
			for(int k=0;k<10;k++){
				if(j!=4)
				dp[i][j]+=dp[i-1][k];
			}
		}
	}
} 
int solve1(int len){
	int ans=0;
	for(int i=len;i>=1;i--){  //从高位到低位处理
		for(int j=0;j<a[i];j++)
			if(j!=4){
				ans+=dp[i][j];
		if(a[i]==4) {
			ans--;break;
		}
	}
	return ans;
}

//记忆化搜索 
int l,r,a[20];
int dp[20];
int dfs(int len,int ismax){
	int ans=0;
	int up;
	if(!len) return 1;
	if(!ismax&&dp[len]!=-1) return dp[len];
	up=ismax? a[len]:9;
	for(int i=0;i<=up;i++){
		if(i==4) continue; 
		ans+=dfs(len-1,ismax&&i==a[len]);
	}
	if(!ismax) dp[len]=ans;
	return ans;
}  
int sovle2(int x){
	int len=0;
	memset(dp,-1,sizeof(dp));
	while(x){
		a[++len]=x%10;
		x/=10;
	}
	return dfs(len,1)	
}
int main(){
return 0;
}

  

 

1、2089 不要62

数字不能出现4和62(连续的)

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1e7+10;
const int INF=0x3fffffff;
typedef long long LL;
LL dp[maxn][2];  //后一维为0表示前面不是6,为1表示前面是6
int l,r;
int len[10];
LL dfs(int pos,int pre,int state,int limit){  
	if(pos==-1) return 1; //当已经到底了,就返回
	if(!limit&&dp[pos][state]!=-1) return dp[pos][state];
	int up=limit? len[pos]:9;
	LL ans=0;
	for(int i=0;i<=up;i++){  //下标从0开始 
		if(i==4) continue;
		if(pre==6&&i==2) continue;
		ans+=dfs(pos-1,i,i==6?1:0,limit&&i==len[pos]);
		//state 为 i 
	}   
	//保存结果 
	if(!limit) dp[pos][state]=ans;
	return ans; 
} 
LL solve(LL x){
	int l=0;
	while(x){
		len[l++]=x%10;
		x/=10;
	}
	return dfs(l-1,0,0,1);  //后面是1 
}
int main(){
	while(scanf("%d %d",&l,&r)){
		if(l==0&&r==0) break;
		memset(dp,-1,sizeof(dp));
		printf("%lld
",solve(r)-solve(l-1));
	}
return 0;
}

2、【HDU6148】Valley Number

数字从左到右看不要有先递增后递减的情况、数位DP题的关键在于如何分析下一个数字的情况

//这道题要考虑前导0的影响
//而且对数字前后的要求
int t;
char s[110];
int mod= 1000000007; 
int a[110];
LL dp[110][10][3];  //分别表示位数、前面的数pre,倾向turn
LL dfs(int pos,int pre,int turn,bool limit,bool inv){ ///turn:0不清楚,1下降,2上升
//这条语句中pos是要DP的位置,pre,turn,limit,invalid...这些都是前提条件,或者
//说是之前位置上确定一些数之后的状态,而这个dfs要进行的就是在这个状态下继续
//确定下一位的数字
	if(pos==-1) return inv? 0:1;
	if(!limit&&dp[pos][pre][turn]!=-1) return dp[pos][pre][turn];
	int up=limit? a[pos]:9;
	LL ans=0LL;  //注意要加LL
	for(int i=0;i<=up;i++){
		if(turn==2&&i<pre) continue; //不能先上升在下降
		int p=0;
		if(i==pre) p=turn;
		else if(i<pre) p=1;
		else p=2;
		if(inv) p=0;  //随时控制前导0 
		ans+=dfs(pos-1,i,p,limit&&i==a[pos],inv&&i==0);
		ans%=mod;
	} 
	ans%=mod;
	if(!limit) dp[pos][pre][turn]=ans;
	return ans;
} 
 
int main(){
	cin>>t;
	while(t--){
		scanf("%s",s);
		memset(dp,-1,sizeof(dp)); 
		int len=strlen(s);
		for(int i=0;i<len;i++) a[i]=s[len-i-1]-‘0‘;
		printf("%lld
",dfs(len-1,0,0,1,1));
	}
return 0;
}

3、HDU 4734

题目给了个f(x)的定义:F(x) = An * 2n-1 + An-1 * 2n-2 + ... + A2 * 2 + A1 * 1,Ai是十进制数位,然后给出a,b求区间[0,b]内满足f(i)<=f(a)的i的个数。

常规想:这个f(x)计算就和数位计算是一样的,就是加了权值,所以dp[pos][sum],这状态是基本的。a是题目给定的,f(a)是变化的不过f(a)最大好像是4600的样子。如果要memset优化就要加一维存f(a)的不同取值,那就是dp[10][4600][4600],这显然不合法。

这个时候就要用减法了,dp[pos][sum],sum不是存当前枚举的数的前缀和(加权的),而是枚举到当前pos位,后面还需要凑sum的权值和的个数,

也就是说初始的是时候sum是f(a),枚举一位就减去这一位在计算f(i)的权值,那么最后枚举完所有位 sum>=0时就是满足的,后面的位数凑足sum位就可以了。
仔细想想这个状态是与f(a)无关的(新手似乎很难理解),一个状态只有在sum>=0时才满足,如果我们按常规的思想求f(i)的话,那么最后sum>=f(a)才是满足的条件。

但是在函数调用的时候

intdfs(int pos,int sum,bool limit) 里面的sum表示的还是当前已经有的数

//减法
int f(int x){
	if(x==0) return 0;
	int ans=f(x/10);
	return ans*2+(x%10);
} 
int dp[10][5000];
int a[12];
int all;
int dfs(int pos,int sum,int limit){
	if(pos==-1) return sum<=all; //注意这里 
	if(sum>all) return 0; //如果大的话,就直接返回
	if(!limit&&dp[pos][all-sum]!=-1) return dp[pos][all-sum];  //减法 
	int up=limit? a[pos]:9;
	int ans=0;
	for(int i=0;i<=up;i++){
		ans+=dfs(pos-1,sum+i*(1<<pos),limit&&i==a[pos]); //计算 
	} 
	if(!limit ) dp[pos][all-sum]=ans;
	return ans;
}
int sovle(int x){
	int len=0;
	while(x){
		a[len++]=x%10;
		x/=10;
	}
	return dfs(len-1,0,1);
}

int main(){
	int t,op=1;
	int a,b;
	scanf("%d",&t);
	memset(dp,-1,sizeof(dp));
	while(t--){
		scanf("%d %d",&a,&b);
		all=f(a);
		//sovle(b);
		printf("Case #%d: %d
",op++,sovle(b));
	}
return 0;
}

4、POJ 3252

这题的约束就是一个数的二进制中0的数量要不能少于1的数量,通过上一题,这题状态就很简单了,dp[pos][num],到当前数位pos,0的数量减去1的数量不少于num的方案数,一个简单的问题,中间某个pos位上num可能为负数(这不一定是非法的,因为我还没枚举完嘛,只要最终的num>=0才能判合法,中途某个pos就不一定了),这里比较好处理,Hash嘛,最小就-32吧(好像),直接加上32,把32当0用。这题主要是要想讲一下lead的用法,显然我要统计0的数量,前导零是有影响的。至于!lead&&!limit才能dp,都是类似的,自己慢慢体会吧

//要考虑前导零的影响,因为题目的要求
//dp[pos][num],到当前数位pos,0的数量减去1的数量不少于num的方案数,一个简单的问题,
//中间某个pos位上num可能为负数(这不一定是非法的,因为我还没枚举完嘛,只要最终的num>=0才能判合法,
//中途某个pos就不一定了),这里比较好处理,Hash嘛,最小就-32吧(好像),直接加上32,把32当0用。这题主要是要想讲一下lead的用法,
//显然我要统计0的数量,前导零是有影响的。!lead&&!limit才能dp
int dp[35][100];
int a[60];
int dfs(int pos,int sta,bool lead,bool limit){  //位数、0-1的数,前导零,上限 
	if(pos==-1) return sta>=32;
	if(!lead&&!limit&&dp[pos][sta]!=-1) return dp[pos][sta];
	int up=limit? a[pos]:1; //上限
	int ans=0;
	for(int i=0;i<=up;i++){
		if(lead&&i==0) //如果有前导零,就略过
		ans+=dfs(pos-1,sta,lead,limit&&i==a[pos]);
		else ans+=dfs(pos-1,sta+(i==0? 1:-1),lead&&i==0,limit&&i==a[pos]); 
	} 
	if(!limit&&!lead) dp[pos][sta]=ans;
	return ans;
}
int solve(LL x){
	int len=0;
	while(x){
		a[len++]=x&1;
		x>>=1;
	}
	return dfs(len-1,32,1,1); //以32为起点,以免中间有负数 
}
int main(){
	memset(dp,-1,sizeof(dp));
	LL a,b;
	scanf("%lld %lld",&a,&b);
	printf("%d
",solve(b)-solve(a-1));
	
	
return 0;
}

  

一本通

1585: 【例 1】Amount of Degrees

技术图片

 

 

 跟上面的减法一样的思想,但是这个也有树的思想

论文和题解:

https://wenku.baidu.com/view/d2414ffe04a1b0717fd5dda8.html

统计区间[0,x]内二进制表示含k个1的数的个数 。统计一棵高度为 i 的完全二叉树内二进制表示中恰好含有 j 个 1的数的个数 

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1010;
const int INF=0x3fffffff;
typedef long long LL;
//这道题感觉理解还是不是很彻底
//https://blog.csdn.net/primoblog/article/details/13168287?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-3&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-3
int dp[32][32],a[32];
int k,b,x,y; 
void inti(){
	dp[0][0]=1;
	for(int i=1;i<=31;i++){
		dp[i][0]=dp[i-1][0];  //有题解提到用完全二叉树理解
		for(int j=1;j<=i;j++)
		dp[i][j]=dp[i-1][j]+dp[i-1][j-1]; 
	}
}
int calc(int xx){ ////统计区间[0,x]内二进制表示含k个1的数的个数 
//统计一棵高度为 i 的完全二叉树内二进制表示中恰好含有 j 个 1的数的个数 
	int tot=0,ans=0,len=0;//tot记录当前路径上已有的1的数量,ans为答案
	while(xx){
		a[++len]=xx%b;
		xx/=b;
	} 
	for(int i=len;i>0;i--){
		if(a[i]==1){
			ans+=dp[i-1][k-(tot++)];
			if(tot==k) break;
		}
		else if(a[i]>1){
			ans+=dp[i][k-tot];
			break;
		}
	}
	return tot==k?ans+1:ans;
}
int main(){
	scanf("%d %d %d %d",&x,&y,&k,&b);
	inti();  //初始化 
	printf("%d
",calc(y)-calc(x-1));
return 0;
}

1586:【 例 2】数字游戏

指定一个整数闭区间 [a,b],问这个区间内有多少个不降数。

int f[32][32]/f[位数i][第i位(最高位)的数字]    的合理情况

递推的做法:

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1010;
const int INF=0x3fffffff;
typedef long long LL;
int f[32][32],a[32];
// //f[位数i][第i位(最高位)的数字] 
void inti(){ //初始化 
	for(int i=1;i<=31;i++) f[1][i]=1;
	for(int i=2;i<=31;i++){
		for(int j=0;j<=9;j++){
			for(int k=j;k<=9;k++){
				f[i][j]+=f[i-1][k];
			}
		}
	}
} 
int solve(int x){
	int len=0;
	memset(a,0,sizeof(a));
	while(x){
		a[++len]=x%10;
		x/=10;
	}
	int ans=0;  //方案数
	for(int i=len;i;i--){
		if(a[i+1]>a[i]) break;  //不降数,下降了
		for(int j=a[i+1];j<a[i];j++){  //不降数,必须比前一个大,比现在这个小 
			ans+=f[i][j];
		}
		if(i==1) ans++; //本身也是 (一位数) 
	} 
	return ans;
}
int main(){
	int x,y;
	inti();
	while(scanf("%d %d",&x,&y)!=EOF){
		printf("%d
",solve(y)-solve(x-1));
	}
	 
return 0;
}

记忆化搜索的做法:

LL dp[20][20][2];   分别表示位第i位,填的数字为j,是否是上界,是否是前导0

#include<bits/stdc++.h>
using namespace std;
const int maxn = 12;
int n,l,r,f[maxn][maxn][2];
char buf[maxn];
int dfs(int pos,int pre,int limit){
	if(pos==n) return 1;
	if(f[pos][pre][limit]) return f[pos][pre][limit];
	int mx=limit?buf[pos]-‘0‘:9,ans=0;
	for( int i=0; i<=mx; i++ ) if(i>=pre) ans+=dfs(pos+1,i,limit&(i==mx));
	return f[pos][pre][limit]=ans;
}
int solve(int num){
	memset(f,0,sizeof(f));
	sprintf(buf,"%d",num),n=strlen(buf);
	return dfs(0,0,1);
}
int main()
{
	while(cin>>l>>r) cout<<solve(r)-solve(l-1)<<‘
‘;
    return 0;
} 

1587: 【例 3】Windy 数

Windy 定义了一种 Windy 数:不含前导零且相邻两个数字之差至少为2的正整数被称为 Windy 数。

递推的做法(感觉要好理解一点)TAT

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1010;
const int INF=0x3fffffff;
typedef long long LL;
//看了一下,觉得这道题用递推更好理解
int f[32][32],a[32];
// //f[位数i][第i位(最高位)的数字] 
void inti(){   //递推都需要初始化 
	for(int i=0;i<=9;i++) f[1][i]=1; //只有1位 
	for(int i=2;i<=31;i++){
		for(int j=0;j<=9;j++){
			for(int k=0;k<=9;k++){
				if(abs(j-k)>=2) f[i][j]+=f[i-1][k];
			}
		}
	}
}
int solve(int x){
	memset(a,0,sizeof(a));
	int len=0;
	while(x){
		a[++len]=x%10;
	//	cout<<a[len]<<" ";
		x/=10;
	}
//	cout<<endl;
	//按照顺序处理位数
	int ans=0;
	for(int i=1;i<len;i++){ //不足len的位的部分 
		for(int j=1;j<=9;j++)  ans+=f[i][j];
		//不能从0开始 
	} 
	for(int i=1;i<a[len];i++) //第len位不足a[len]的部分
	ans+=f[len][i];
	//然后处理第len位为a[len]的数据,因为是很大的树,所以要控制上限
	for(int i=len-1;i;i--){
		for(int j=0;j<a[i];j++){ //最高位已经确定了,所以可以取到0了 
			if(abs(j-a[i+1])>=2) ans+=f[i][j]; ////跟前一位比较
		}
		if(abs(a[i+1]-a[i])<2) break; 
		if(i==1) ans++; //?因为上面处理 不足len的位的部分 的时候没有处理1位 
	}
	 return ans; 
}

int main(){
	inti();
	int x,y;
	cin>>x>>y;
	cout<<solve(y)-solve(x-1)<<endl;
return 0;
}

记忆化搜索  四维  dp[i][j][Bo1][Bo2]第i位,填的数字为j,是否是上界,是否是前导0

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1010;
const int INF=0x3fffffff;
typedef long long LL;
int a,b,len;
int dp[30][30][2][2];
int num[30];
//https://www.cnblogs.com/gaojunonly1/p/10360015.html
//https://blog.csdn.net/qq_42367531/article/details/82526133
//windy数,也是很友好的数位dp,也像数字游戏一样搞一搞,dp[i][j][Bo1][Bo2]第i位,填的数字为j,是否是上界,是否是前导0就over了
int dfs(int pos,int pre,int limit,int inv){  //位数,前面的数,上界,前导零
	if(pos==1) return dp[pos][pre][limit][inv]=1;
	if(dp[pos][pre][limit][inv])  return dp[pos][pre][limit][inv];
	int up=limit? num[pos-1]:9;
	//int ans=0;
	for(int i=0;i<=up;i++){
		if(inv||abs(pre-i)>1){
			int b1=(limit&&i==up);  //判断方式!! 
			int b2=(inv&&i==0);
			dp[pos][pre][limit][inv]+=dfs(pos-1,i,b1,b2);
		}
	} 
  	return dp[pos][pre][limit][inv];
}
int solve(int n){
	if(n==0) return 1;
	memset(dp,0,sizeof(dp));
	len=0;
	while(n){
		num[++len]=n%10;
		n/=10;
	}
	int ans=0;
	ans+=dfs(len,0,0,1);  //
	for(int i=1;i<num[len];i++){
		ans+=dfs(len,i,0,0);
	} 
	ans+=dfs(len,num[len],1,0);
	return ans;
} 
/*
//只用二维
////f[第i高位][填的数字]
int f[10][10]; 
int dfs(int pos,int pre,int inv,int limit){
	if(pos==0) return 1;
	if(limit==0&&dp[pos][inv]!=-1) return dp[pos][inv];
	int ans=0,up;
	up=limit? a[pos]:9;
	for(int i=0;i<=up;i++){
		if(limit==1){  //有前导零 
			int zz=(i==0)?1:0;  //inv的设置也为zz 
			if(limit==1&&i==up) ans+=dfs(pos-1,i,zz,1);  //上界限制为1 
			else ans+=dfs(pos-1,i,zz,0);     //上界限制为0 
		}
		else if(abs(pre-i)>=2){
			if(limit&&i==up) ans+=dfs(pos-1,i,0,1);
			//前导零为0,但是上界限制为1
			else ans+=dfs(pos-1,i,0,0);
			//上界限制也为0 
		}
	}
	if(!limit&&!inv) f[len][pre]=ans;
	return ans;
}
//调用的时候
return dfs(len,0,1,1); 
*/


int main(){
	scanf("%d %d",&a,&b);
	printf("%d",solve(b)-solve(a-1));
return 0;
}

1588:数字游戏

某人又命名了一种取模数,这种数字必须满足各位数字之和mod N=0 。现在大家又要玩游戏了,指定一个整数闭区间[a,b],问这个区间内有多少个取模数。

这道题还比较简单,但是要注意函数写法,返回的是summ==0;  //返回一个判断

ans+=dfs(pos+1,(summ+i)%k,limit&&i==up);

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1010;
const int INF=0x3fffffff;
typedef long long LL;
int l,r,k;
int dp[40][110];
int num[40];
int len; 
int dfs(int pos,int summ,int limit){
	if(pos>len) return summ==0;  //返回一个判断
	if(dp[pos][summ]!=-1&&!limit) return dp[pos][summ]; 
	int up=limit?num[len-pos+1]:9;
	int ans=0;
	for(int i=0;i<=up;i++){
		ans+=dfs(pos+1,(summ+i)%k,limit&&i==up);
	}
	if(!limit) dp[pos][summ]=ans;
	return ans;
}
int solve(int x){
	memset(num,0,sizeof(num));
	memset(dp,-1,sizeof(dp));
	len=0;
	while(x){
		num[++len]=x%10;
		x/=10;
	}
	return dfs(1,0,1);
}
int main(){
	
	while(~scanf("%d %d %d",&l,&r,&k)){
		printf("%d
",solve(r)-solve(l-1));
	}
	
return 0;
}

1590:恨 7 不成妻

好难TAT  https://blog.csdn.net/deerly_/article/details/79930085

需要维护三个值   --->定义结构体,假定dfs推出返回的结构体是tmp,当前结果的结构体是ans 

1.符合条件数的个数 cnt 

2.符合条件数的和 sum 

3.符合条件数的平方和 sqr 

三个条件

(1)数中某一位是 7;   基础的数位dp很好维护;

(2)整数的每一位加起来的和是7 的整数倍;

tmp.sum * 10 + (10 ^ pos * i) * ans.cnt 就是上一步状态的和加上这一步加的 

这一步加的就是10的当前位次方乘以i,因为有ans.cnt个嘛,所以再乘以ans.cnt 

(3)这个整数是 7 的整数倍。

3 首先重新构建一下这个数 (10^pos * i + x)x是这个数的后面部分,就是上一次状态得到的那个数,则平方和就是(10^len*i)^2+x^2+2*10^len*i*x, 其中x^2=tmp.sqr; 

ans.sqr += (2*10^pos*i*x)*tmp.cnt=(2*10^pos*i)*next.sum(神奇的化简) 

ans.sqr += (10^pos*i)^2*tmp.cnt;

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1010;
const int INF=0x3fffffff;
const long long MOD = 1e9 + 7;
typedef long long LL;
//好难啊
/*
整数中某一位是 77;
整数的每一位加起来的和是 77 的整数倍;
这个整数是 77 的整数倍。
首先这三个条件都是基础数位dp,就不说了。 
重点是怎么去求平方和。 
需要维护三个值 
1.符合条件数的个数 cnt 
2.符合条件数的和 sum 
3.符合条件数的平方和 sqr 
为什么要维护这三个呢,接着往下看你就知道了 
假定dfs推出返回的结构体是tmp,当前结果的结构体是ans 
其中1是基础的数位dp很好维护; 
2 tmp.sum * 10 + (10 ^ pos * i) * ans.cnt 就是上一步状态的和加上这一步加的 
这一步加的就是10的当前位次方乘以i,因为有ans.cnt个嘛,所以再乘以ans.cnt 
3 首先重新构建一下这个数 (10^pos * i + x)x是这个数的后面部分,就是上一次状态得到的那个数,则平方和就是(10^len*i)^2+x^2+2*10^len*i*x, 其中x^2=tmp.sqr; 
ans.sqr += (2*10^pos*i*x)*tmp.cnt=(2*10^pos*i)*next.sum(神奇的化简) 
ans.sqr += (10^pos*i)^2*tmp.cnt;
原文链接:https://blog.csdn.net/deerly_/article/details/79930085
*/ 
LL p[25];  //这个是位数,1 10 100 1000 10000这种
LL num[40];
struct node{  //要用到结构体 
	LL cnt,summ,sqr;   
	//符合条件数的个数    符合条件数的和    符合条件数的平方和
	node(){cnt=-1,summ=sqr=0;}
	node(LL cnt,LL summ,LL sqr)  : cnt(cnt),summ(summ),sqr(sqr){}
}dp[20][20][20]; 
LL t,l,r,len;
node dfs(int pos,int sum1,int sum2,bool limit){  //sum1为每一位加起来的和,sum2 
	if(pos==0){
		if(sum1&&sum2){
			return node(1,0,0); //个数为1 
		}
		 return node(0,0,0);
	}
	if(!limit&&dp[pos][sum1][sum2].cnt!=-1) return dp[pos][sum1][sum2];
	int up=limit? num[pos]:9;
	node ans;
	ans.cnt=0;
	for(int i=0;i<=up;i++){
		if(i==7) continue;
		node temp=dfs(pos-1,(i+sum1)%7,(sum2*10+i)%7,limit&&i==up);
		ans.cnt+=temp.cnt; //这个可以直接加,很好维护 
		ans.cnt%=MOD;
		ans.summ+=(temp.summ+((p[pos]*i)%MOD)*temp.cnt%MOD)%MOD;
		ans.summ%MOD;
		//看看这个怎么计算的 
		ans.sqr+=(temp.sqr+((2*p[pos]*i)%MOD)*temp.summ)%MOD;
		ans.sqr%=MOD;
		ans.sqr+=((temp.cnt*p[pos])%MOD*p[pos]%MOD*i*i%MOD);
		ans.sqr%=MOD;
	}
	if(!limit) dp[pos][sum1][sum2]=ans;
	return ans; 
}
LL solve(LL n){
	len=0;
	while(n){
		num[++len]=n%10;
		n/=10;
	}
	node v=dfs(len,0,0,1);
	return v.sqr;
}


int main(){
	scanf("%d",&t);
	p[1]=1;
	for(int i=2;i<=20;i++) p[i]=(p[i-1]*10)%MOD;
	while(t--){
		scanf("%lld %lld",&l,&r);
		LL ans=solve(r);
		ans-=solve(l-1);
		printf("%lld
",(ans%MOD+MOD)%MOD);
	}
return 0;
}

1591:数字计数

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

10次dfs

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1010;
const int INF=0x3fffffff;
typedef long long LL;
LL l,r;
LL len;
LL dp[20][110];
LL num[20];
LL dfs(int pos,int dig,LL summ,int lead,int limit){
	//位数   上一个数字   方案数  前导0   上界
	if(pos>len) return summ;
	if(dp[pos][summ]!=-1&&!limit&&!lead) return dp[pos][summ];
	int up=limit? num[len-pos+1]:9;  //是递增的 
	LL ans=0;
	for(int i=0;i<=up;i++){
		if(lead&&i==0) //有前导零 
		ans+=dfs(pos+1,dig,0,1,up==i&&limit); //方案数位0 
		else
		ans+=dfs(pos+1,dig,summ+(i==dig?1:0),0,i==up&&limit);  //是对应的数字的话,那么方案数+1 
	}
	if(!limit&&!lead) dp[pos][summ]=ans;
	return ans;
}
LL work(LL x,int dig){																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																				
	memset(dp,-1,sizeof(dp));
	len=0;
	while(x){
		num[++len]=x%10;
		x/=10;
	}
	return dfs(1,dig,0,1,1);
}
int main(){
	scanf("%lld %lld",&l,&r);
	if(l){
		for(int i=0;i<=9;i++){
			printf("%lld ",work(r,i)-work(l-1,i));
		}
	}
	else{
		for(int i=0;i<=9;i++){
			printf("%lld ",work(r,i)-work(l,i));
		}
	}
return 0;
}

  

 

 

#include<iostream>#include<cstring>#include<cmath>#include<algorithm>#include<stack>#include<cstdio>#include<queue>#include<map>#include<vector>#include<set>using namespace std;const int maxn=1e7+10;const int INF=0x3fffffff;typedef long long LL;LL dp[maxn][2];  //后一维为0表示前面不是6,为1表示前面是6int l,r;int len[10];LL dfs(int pos,int pre,int state,int limit){  if(pos==-1) return 1; //当已经到底了,就返回if(!limit&&dp[pos][state]!=-1) return dp[pos][state];int up=limit? len[pos]:9;LL ans=0;for(int i=0;i<=up;i++){  //下标从0开始 if(i==4) continue;if(pre==6&&i==2) continue;ans+=dfs(pos-1,i,i==6?1:0,limit&&i==len[pos]);//state 为 i }   //保存结果 if(!limit) dp[pos][state]=ans;return ans; } LL solve(LL x){int l=0;while(x){len[l++]=x%10;x/=10;}return dfs(l-1,0,0,1);  //后面是1 }int main(){while(scanf("%d %d",&l,&r)){if(l==0&&r==0) break;memset(dp,-1,sizeof(dp));printf("%lld ",solve(r)-solve(l-1));}return 0;}

以上是关于数位DP的主要内容,如果未能解决你的问题,请参考以下文章

数位dp小练

动态规划_计数类dp_数位统计dp_状态压缩dp_树形dp_记忆化搜索

数位DP

数位dp

HDU 2089 数位dp入门

数位DP