数位dp。。。

article/2025/9/13 12:05:21

(建议认真学的人最好别看我的博客)
https://blog.csdn.net/KonnyWen/article/details/104475276别人的一篇博客,明天早上学!
好的我们开始:数位 dp 是指求在数位限制下有多少满足要求的数的 dp \texttt{dp}dp。例如,求“在 [ L , R ] 范围内连续出现过 3 个 3 的数”,“相邻两位之间差为质数的 5 位数”或“在 [ L , R ] 区间内 6 出现的次数”。通常来说会有两种的:递推以及记忆化搜索。
P2602 [ZJOI2010] 数字计数我们选择使用递推,可以用sum[i]表示到i位的时候,1~9的每一个数量之和,仔细想想因为只限定了位数没限定大小,那么其实1 ~ 9的数量都是相等的,0我们稍后考虑算。那么如何从前一位推上来呢,那么其实就是sum[ i ] = sum[ i-1 ]*10+10^(i-1),(i>=2)然后sum[1] 初定为1,那么就可以处理出sum,搞定预处理之后,那么我们考虑[1,n]的出现次数。我们先用一个数组记录下n每一位的数字!然后从最高位往前做,对于一个ABCD这样的数字,分三种情况讨论A的贡献:
1.目前枚举到的数字j<A ,那么其实就相当于前面的所有和都能和上面一样转移上来;
2.目前枚举到的数字j==A,那么只能转移一部分,也就是BCD+1这一部分;
3.那么超过A的部分其实是不用算的。
然后每次往前推进一格,一直推到最低位
其实这样考虑是不完全的,我们应该考虑目前这一位,会对前面每一个数重新造成一次影响,就像已经计算过了一次123,欸~我们有1123,2123,所以要重新计算一次前面的数,但只能计算num[i]次,然后考虑前面的数对这一位能造成的贡献,那么就像前面讨论说的那样分三种。
然后考虑0,那么0其实就是所有情况减去前导0,那么怎么算呢考虑一下,若是在第三位然后前导0的数量就是0~99的数量便是10^(i-1)。在这里插入图片描述别人的博客看吧看吧。

#include<bits/stdc++.h>
#define int long long 
using namespace std;
int n,m,cnt[101],ten[101];//cnt是1~9在每一位的计数 
void init()
{ten[0]=1;for(int i=1;i<=12;i++){ten[i]=ten[i-1]*10;//预处理10^i cnt[i]=cnt[i-1]*10+ten[i-1];//预处理每一位的值,注意不是前缀和哈,是本身的贡献 }return ;
}
int num[10001];
void make(int x,int f[])
{int k=x,len=0;while(x) num[++len]=x%10,x/=10;for(int i=len;i>=1;i--){for(int j=0;j<=9;j++) f[j]+=cnt[i-1]*num[i];//对前面每一位都会造成贡献 for(int j=0;j<=num[i]-1;j++) f[j]+=ten[i-1];//前面i-1为对i位为j时的贡献 k-=num[i]*ten[i-1];f[num[i]]+=k+1;//算上num[i]0000的情况f[0]-=ten[i-1]; }return ;
}	
int f1[11],f2[11];
signed main()
{int a,b;scanf("%lld%lld",&a,&b);init();make(a-1,f1);make(b,f2);for(int i=0;i<=9;i++) printf("%lld ",f2[i]-f1[i]); return 0;
} 

然后做一道寄搜,P2657 [SCOI2009] windy 数,我抄的那个还是麻烦了,他选择的是先用递推算除最高位的所有答案,然后再从最高位开始找起。在这里插入图片描述

#include<bits/stdc++.h> 
#define int long long  
using namespace std;
int n,m,f[18][18],g[18][18],num[18];
void init()
{for(int i=0;i<=9;i++) f[1][i]=1;for(int i=2;i<=10;i++){for(int j=0;j<=9;j++){for(int J=0;J<=9;J++) {if(abs(j-J)>=2) f[i][j]+=f[i-1][J];}	} } return ;
}
int dfs(int w,int d,bool free)
{if(!w) return 1;if(free&&~g[w][d]) return g[w][d];int up=free?9:num[w],rt=0;for(int i=0;i<=up;i++) {if(abs(i-d)>=2) rt+=dfs(w-1,i,free||i<up);}if(free) g[w][d]=rt;return rt;
} 
int dp(int x)
{int len=0,ans=0;memset(g,-1,sizeof(g));while(x) num[++len]=x%10,x/=10;for(int i=1;i<=num[len];i++) ans+=dfs(len-1,i,i<num[len]);for(int i=len-1;i>=1;i--){for(int j=1;j<=9;j++) ans+=f[i][j];//printf("%lld\n",ans);}return ans;
}
main()
{int a,b;scanf("%lld%lld",&a,&b);init();printf("%lld\n",dp(b)-dp(a-1));return 0;
}

决定重新打一个板子:

#include<bits/stdc++.h>
#define int long long 
using namespace std;
int n,m,f[1001][11],num[100001];//我们用f[i,j]表示搜到i,当前的值(i-1)值是j
int dfs(int len,int x,bool free,bool zer)
{if(!len) return 1;if(!free&&!zer&&f[len][x]!=-1) return f[len][x];int up=9,rt=0;if(free) up=num[len];for(int i=0;i<=up;i++){if(abs(x-i)<2&&!zer) continue ;if(zer&&i==0) rt+=dfs(len-1,i,i==up&&free,true);else rt+=dfs(len-1,i,i==up&&free,false);}if(!free&&!zer) f[len][x]=rt;return rt;
}
int dp(int x)
{int len=0;memset(f,-1,sizeof(f));while(x) num[++len]=x%10,x/=10;return dfs(len,0,true,true);
}
main()
{int a,b;scanf("%lld%lld",&a,&b);printf("%lld",dp(b)-dp(a-1));return 0;
}

欸P4999 烦人的数学作业这一题!是一个比较简单的数位dp,我决定摆烂。

#include<bits/stdc++.h>
#define int long long 
using namespace std;
int n,m,mod=1e9+7,cnt[100],ten[101];
void init()
{ten[0]=1;for(int i=1;i<=20;i++) {ten[i]=(ten[i-1]*10)%mod;cnt[i]=(cnt[i-1]*10+ten[i-1])%mod;}return ;
}
int num[100];
void make(int x,int f[])
{int k=x,len=0;while(x) num[++len]=x%10,x/=10;//printf("<<%lld",len); for(int i=len;i>=1;i--){for(int j=0;j<=9;j++) f[j]=(f[j]+cnt[i-1]*num[i])%mod;for(int j=0;j<=num[i]-1;j++) f[j]=(f[j]+ten[i-1])%mod;k-=num[i]*ten[i-1];f[num[i]]=(f[num[i]]+k+1)%mod;}return ;
}
int f1[100],f2[100];
signed main()
{int t;scanf("%lld",&t);init();while(t--){int a,b,ans=0;scanf("%lld%lld",&a,&b);memset(f1,0,sizeof(f1));make(a-1,f1);memset(f2,0,sizeof(f2));make(b,f2);for(int i=0;i<=9;i++) ans=(ans+(f2[i]-f1[i])*i%mod+mod)%mod;printf("%lld\n",ans);}return 0;
}

P6218 [USACO06NOV] Round Numbers S
相对而言,我认为寄搜的方式会更好理解些。那么你需要注意的地方是,前导0以及是否为最大值,若是的话就要单独计算,若不是,那就直接继承之前的值。

#include<bits/stdc++.h>
#define int long long 
using namespace std;
int n,m,f[101][110][101];//剩下a位,b个1,c个0 (其实可以化简) 
int num[100001],mod=1e7+7;
int dfs(int len,int sum0,int sum1,bool free,bool zero)//free指的是前一位能否取到最大值,zero指的是是否有前导0 
{if(len==0){//printf("%lld %lld %lld\n",len,sum0,sum1);if(sum1<=sum0) return 1;else return 0;}if(!free&&!zero&&f[len][sum0][sum1]!=-1) return f[len][sum0][sum1];int up=1,rt=0;if(free) up=num[len];for(int i=0;i<=up;i++){if(zero&&i==0) rt+=dfs(len-1,0,0,free && i==up,true);else rt+=dfs(len-1,sum0+(i==0),sum1+(i==1),free && i==up,false);}if(!free&&!zero) f[len][sum0][sum1]=rt;return rt; 
}
int dp(int x)
{int len=0;memset(f,-1,sizeof(f));	 while(x) num[++len]=x&1,x>>=1;//printf("%lld\n",len);return dfs(len,0,0,true,true);
}
main()
{int a,b;scanf("%lld%lld",&a,&b);printf("%lld",dp(b)-dp(a-1));return 0;
}

好像,我大概也懂了些寄搜罢P1836 数页码

#include<bits/stdc++.h>
#define int long long 
using namespace std;
int n,m,num[10001],f[12][10001];
int dfs(int len,int sum,bool free,bool zer)
{if(len==0) return sum;if(!free&&!zer&&f[len][sum]!=-1) return f[len][sum];int up=9,rt=0;if(free) up=num[len];for(int i=0;i<=up;i++) rt+=dfs(len-1,sum+i,free&(i==up),zer	&(i==0));if(!free&&!zer) f[len][sum]=rt;return rt;
}
int dp(int x)
{int len=0;memset(f,-1,sizeof(f));	while(x) num[++len]=x%10,x/=10;return dfs(len,0,true,true);
}
main()
{int x;scanf("%lld",&x);printf("%lld",dp(x));return 0;	
}

P4317 花神的数论题这个tm裸的但是模的很阴间然后数组要开50你敢信。

#include<bits/stdc++.h>
#define int long long 
using namespace std;
int n,m,num[1000001],f[50][100001],mod=10000007;
int dfs(int len,int x,bool free,bool zer)
{if(!len) return max(x,1ll);if(!free&&!zer&&f[len][x]!=-1) return f[len][x]%mod;int up=1,rt=1;if(free) up=num[len];for(int i=0;i<=up;i++) rt=(rt*dfs(len-1,x+i,free&i==up,zer&&i==0))%mod;if(!free&&!zer) f[len][x]=rt;return rt;
}
int dp(int x)
{int len=0;memset(f,-1,sizeof(f));while(x) num[++len]=x&1,x>>=1;return dfs(len,0,true,true);
}
signed main()
{int x;scanf("%lld",&x);printf("%lld",dp(x));return 0;
}

其实做了这么多题,都只是为了补一题:Digit Sum草走忽略,这东西有东西的,不干净,我不知道调了半天都不知道为什么错。

#include<bits/stdc++.h>
#define int long long 
using namespace std;
int n,m,k,num[20001],f[20001][1000],mod=1e9+7;
int dfs(int len,int x,bool free,bool zer)
{if(len==0) return (x%k==0);if(!free&&!zer&&f[len][x%k]!=-1) return f[len][x%k];int up=9,rt=0;if(free) up=num[len];for(int i=0;i<=up;i++) rt=(rt+dfs(len-1,(x+i)%k,free & (i==up),zer & (i==0)))%mod;if(!free&&!zer) f[len][x%k]=rt%mod;return rt%mod;
}
int dp(char x[])
{int len=strlen(x+1);memset(f,-1,sizeof(f));for(int i=1;i<=len;i++) num[len-i+1]=x[i]-'0';return dfs(len,0,true,true);
}
signed main()
{char x[100001];scanf("%s%lld",x+1,&k);int ans=dp(x)%mod;
//	if(ans==0) printf("0");
//	else printf("%lld",(ans-1+mod)%mod);return 0;
}

Classy Numbers

#include<bits/stdc++.h>
#define int long long 
using namespace std;
int n,m,num[101],f[100][1001];
int dfs(int len,int st,bool free,bool zer)
{if(st>3) return 0;if(len==0) return 1;if(!free&&!zer&&f[len][st]!=-1) return f[len][st];int up=9,rt=0;if(free) up=num[len];for(int i=0;i<=up;i++) rt+=dfs(len-1,st+(i!=0),free & i==num[len],zer & (i==0));if(!zer&&!free) f[len][st]=rt;return rt;
}
int dp(int x)
{int len=0;memset(f,-1,sizeof(f));while(x) num[++len]=x%10,x/=10;return dfs(len,0,true,true);
}
signed main()
{int t;scanf("%lld",&t);while(t--) {int l,r;scanf("%lld%lld",&l,&r);printf("%lld\n",dp(r)-dp(l-1));}return 0;
}

然后又重温一遍板子统计问题 The Counting Problem

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,cnt[10001],ten[10001];
int num[10001],f1[10001],f2[10001];
void init()
{ten[0]=1;for(int i=1;i<=14;i++){ten[i]=ten[i-1]*10;cnt[i]=cnt[i-1]*10+ten[i-1];}return ;
}
void dp(int x,int f[])
{int len=0,k=x;while(x) num[++len]=x%10,x/=10;//printf("%lld ",len);for(int i=len;i>=1;i--){for(int j=0;j<=9;j++) f[j]+=cnt[i-1]*num[i];for(int j=0;j<=num[i]-1;j++) f[j]+=ten[i-1];k-=num[i]*ten[i-1];f[num[i]]+=k+1;f[0]-=ten[i-1];}return ;
}
main()
{int l,r;init();while(scanf("%lld%lld",&l,&r)){if(l==0&&r==0) break;if(l>r) swap(l,r);memset(f1,0,sizeof(f1));dp(l-1,f1);memset(f2,0,sizeof(f2));dp(r,f2);for(int i=0;i<=8;i++) printf("%lld ",f2[i]-f1[i]);printf("%lld\n",f2[9]-f1[9]);}return 0;	
} 

dfs大法好啊~Salazar Slytherin’s Locket,欸明天就能去看她咯~,害可惜刚刚没有看到呢,不知道人去哪了。好吧刚刚uke的原因是应该开多维的数组来保存每一进制的值,所以这样就可以只初始化一次就行啦。

//观题,最多区十位之数,直接压成二进制,若是最终为0,便可输出,哦对记得0不能算在内; 
#include<bits/stdc++.h>
#define int long long 
using namespace std;
int n,m,f[12][100][2001],num[20001],b;
int dfs(int len,int x,bool free,bool zer)
{ if(len==0) return !x;if(!free&&!zer&&f[b][len][x]!=-1) return f[b][len][x];	int up=b-1,rt=0;if(free) up=num[len];for(int i=0;i<=up;i++){if(i==0&&zer) rt+=dfs(len-1,x,free & i==up,zer & i==0);else rt+=dfs(len-1,x^(1<<i),free & i==up,zer & i==0);}if(!free&&!zer) f[b][len][x]=rt;//printf("%lld\n",rt);return rt;
}
int dp(int x)
{int len=0;//memset(f,-1,sizeof(f));while(x) num[++len]=x%b,x/=b;return dfs(len,0,true,true);
}
main() 
{memset(f,-1,sizeof(f));int t;scanf("%lld",&t);while(t--){int x,y;scanf("%lld%lld%lld",&b,&x,&y);printf("%lld\n",dp(y)-dp(x-1));}return 0;
}

http://chatgpt.dhexx.cn/article/rGtKfRKf.shtml

相关文章

数位dp + 记忆化搜索

这里写目录标题 数位dp记忆化搜索例题 数位dp 1.数位dp的由来&#xff1a;数位dp也是动态规划的一种类型&#xff0c;而数位dp解决的问题往往是这样的&#xff1a; 题目会给你一个区间&#xff0c;然后让你去根据这个区间去找一些符合条件的数据。 但是这样的话我们最可能想到…

-数位DP

目录 1、 数位和 2、不要62 3、数数1 4、数数2 5、数数3 1、 数位和 // 数位和#include<bits/stdc.h> using namespace std; int c[21]; long long l, r, ans[10], f[17];inline void calc(long long n, int xs){ // xs : 系数int m 0; while(n){c[m]…

数位DP 详解+模版

首先清楚数位DP要解决什么样的问题&#xff1a; 求出在给定区间 [A,B] 内&#xff0c;符合条件 f(i) 的数 i 的个数。条件 f(i) 一般与数的大小无关&#xff0c;而与数的组成有关。由于数是按位dp&#xff0c;数的大小对复杂度的影响很小。 用记忆化搜索来实现。 先来看模板…

数位DP~

综述 数位DP的应用范围&#xff1a; 在某个区间内有多少个满足一定的性质 数位DP中使用的方法&#xff1a; 类似于前缀和。A到B相当于f[B] - a[A-1] 这一点尤为重要&#xff0c;因为已经弱化了边界&#xff0c;使得考虑的更少分情况讨论 ​ 1081. 度的数量 ​ 输入样例…

数位dp(模板)

数位dp问题题型往往是这样的&#xff1a; 给定一个区间[L,R]&#xff0c;求这个区间中满足“某种条件”的数的总数。 题目&#xff1a;求区间[L,R]范围内有多少带3的数&#xff0c;所谓带3的数就是这个数十进制表示中存在至少一位3 比如3,&#xff0c;123,3333,都是带3的数&…

数位DP讲解

转载自&#xff1a;http://www.cnblogs.com/itlqs/p/5935308.html 数位DP其实是很灵活的&#xff0c;所以一定不要奢求一篇文章就会遍所有数位DP的题&#xff0c;这一篇只能是讲清楚一种情况&#xff0c;其他情况遇到再总结&#xff0c;在不断总结中慢慢体会这个思想&#xff0…

数位dp入门详解

基础篇 数位dp是一种计数用的dp&#xff0c;一般就是要统计一个区间[le,ri]内满足一些条件数的个数。所谓数位dp&#xff0c;字面意思就是在数位上进行dp咯。数位还算是比较好听的名字&#xff0c;数位的含义&#xff1a;一个数有个位、十位、百位、千位......数的每一位就是数…

数位dp。

一&#xff0c;思想&#xff1a; 在处理1e9甚至1e18,1e100的问题时&#xff0c;因为在统计情况下有很多重复的计算&#xff0c;数位dp实现了相同状态只计算一次&#xff0c;从而大幅减少运算时间&#xff0c;思想就是对每一位进行dp&#xff0c;计算时记忆化每一位可以有的状态…

【进阶】数位DP详解

如果想了解更多内容&#xff0c;欢迎关注我的微信公众号&#xff1a;信息学竞赛从入门到巅峰。 戳这里获得更好的阅读体验哦 https://mp.weixin.qq.com/s/eZHoI7RZOvlEhhSNRpGhxA 今天&#xff0c;我向大家介绍一种特殊的DP类型——数位DP。 数位DP这类题目一般不会出现在提高…

超详细讲解数位DP

什么是数位dp 数位dp是一种计数用的dp&#xff0c;一般是要统计一个区级[l,r]内满足一些条件的数的个数 所谓数位dp&#xff0c;就是对数位进行dp&#xff0c;也就是个位、十位等 相对于普通的暴力枚举&#xff0c;数位dp快就快在它的记忆化&#xff0c;也就是说后面可能会利…

数位DP,看这一篇就足够了!

数位DP用来解决什么问题&#xff1f; 我们有时候会遇到这样一类题目&#xff0c;给你一个区间 [l,r] &#xff0c;找区间上符合某种特定要求的数的个数&#xff0c;这个要求可能很简单&#xff0c;很好理解&#xff0c;但是由于区间范围太大&#xff0c;以至于对每个数进行遍历…

数位dp介绍

不了解dp的可以先看一下dp 数位dp含义&#xff1a; 数位&#xff1a;一个数有个位&#xff0c;十位&#xff0c;百位&#xff0c;千位等等&#xff0c;数的每一位都是数位。 数位dp归为计数dp&#xff0c;是在数位上进行操作的dp。 数位dp的实质是一种快速枚举的方式&#xff0…

动态规划——数位dp

数位dp 文章目录 数位dp概述题目特征基本原理计数技巧 模板例题度的数量思路代码 数字游戏思路代码 不要62思路代码 概述 数位是指把一个数字按照个、十、百、千等等一位一位地拆开&#xff0c;关注它每一位上的数字。如果拆的是十进制数&#xff0c;那么每一位数字都是 0~9&am…

数位DP学习整理(数位DP看完这篇你就会了)

文章目录 数位DP数位DP介绍数位DP解法数位DP经典例题例题1&#xff1a;度的数量例题2&#xff1a;计数问题例题3&#xff1a;数字游戏例题4&#xff1a;windy数例题5&#xff1a;数字游戏Ⅱ例题6&#xff1a;不要62例题7&#xff1a;恨7不成妻 数位DP总结 数位DP 数位DP介绍 …

Mysql悲观锁和乐观锁区别

1、mysql悲观锁&#xff1a;在整个数据处理过程中&#xff0c;将数据处于锁定状态。悲观锁的实现&#xff0c;依靠数据库提供的锁机制&#xff0c;每次会申请锁并加锁和解锁操作 第一步&#xff1a;两个终端均关闭自动提交 左边&#xff1a; 右边&#xff1a; 第二步&#xff1…

java的乐观锁和悲观锁

参考&#xff1a; https://www.cnblogs.com/jyroy/p/11365935.html https://www.jianshu.com/p/ae25eb3cfb5d 乐观锁和悲观锁 乐观锁和悲观锁是一种广义上的概念&#xff0c;体现了看待线程同步的不同角度。 乐观锁&#xff1a;对于并发操作产生的线程安全问题持乐观态度&…

MySQL中悲观锁和乐观区别

1、概念不同 乐观锁( Optimistic Locking)&#xff1a; 顾名思义&#xff0c;对加锁持有一种乐观的态度&#xff0c;即先进行业务操作&#xff0c;不到最后一步不进行加锁&#xff0c;"乐观"的认为加锁一定会成功的&#xff0c;在最后一步更新数据的时候再进行加锁…

PyQt5 - 双QTimer实现并行输出

QTime的使用 双Qtime的实现原理 一&#xff1a;QTime的使用 # -*- coding: utf-8 -*-# Form implementation generated from reading ui file D:\Qt\QT-Projects\UI项目\时间实时更新.ui # # Created by: PyQt5 UI code generator 5.12.2 # # WARNING! All changes made in t…

QTimer 定时器

QTimer类为我们提供了一个即可重复触发又可单次触发的定时器。它是一个高层次的应用程序接口。要使用它&#xff0c;只需创建一个QTimer类对象&#xff0c;将它的timeout&#xff08;&#xff09;信号连接到适当的函数上&#xff0c;然后调用其start&#xff08;&#xff09;函…

QTimer使用

QTimer工作流程 程序示例 test_timee.h //继承QObject&#xff0c;使用信号/槽 class TestTimer: public QObject {Q_OBJECT public:TestTimer(QObject *parent nullptr);public slots:void start();void stop();void do1();private://定义timer对象QTimer m_timer; }; tes…