数位dp

AcWing 311. 月之谜 数位dp

帅比萌擦擦* 提交于 2020-03-30 22:46:17
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> using namespace std; #define int long long typedef long long ll; const int N=200; int read() { int res=0,ch,flag=0; if((ch=getchar())=='-') //判断正负 flag=1; else if(ch>='0'&&ch<='9') //得到完整的数 res=ch-'0'; while((ch=getchar())>='0'&&ch<='9') res=res*10+ch-'0'; return flag?-res:res; } int mod; int l,r,a[N],dp[N][N][N]; int dfs(int pos,int sum,int now,int limit) { if(!pos) return (sum==mod)&&(now==0); if(!limit&&dp[pos][sum][now]!=-1) return dp[pos][sum][now]; int up=limit?a[pos]:9; int res=0; for(int i=0;i<=up;i++) res+=dfs

算法笔记--数位dp

≡放荡痞女 提交于 2020-03-30 02:25:32
算法笔记 这个博客写的不错: http://blog.csdn.net/wust_zzwh/article/details/52100392 数位dp的精髓是不同情况下sta变量的设置。 模板: 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

数位DP

蓝咒 提交于 2020-03-28 20:40:25
这篇博客主要是数位DP的模版 基本上数位DP的题目都是比较套路的:一般都是问一个区间中满足条件的数的个数 套路1:差分 [L,R]中的答案 = [0,R]中答案 - [0,L]中答案 + chk(L) 套路2:按位DP(记忆化搜索实现),记录需要的前缀状态,特别的: f = 0/1 代表当前填的数字是否还和上界数字相同 g = 0/1 代表当前是否还在填前导零 复杂度一般为 O(位数*前缀状态数*10) 【P2657 [SCOI2009]windy数】 https://www.luogu.com.cn/problem/P2657 不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。 windy想知道,在A和B之间,包括A和B,总共有多少个windy数? #pragma GCC optimize(3,"Ofast","inline")//O3优化 #pragma GCC optimize(2)//O2优化 #include <algorithm> #include <string> #include <string.h> #include <vector> #include <map> #include <stack> #include <set> #include <queue> #include <math.h> #include <cstdio> #include

【数位DP】 CF 55D Beautiful numbers

情到浓时终转凉″ 提交于 2020-03-26 21:58:39
通道: http://codeforces.com/problemset/problem/55/d 题意: 求 [l,r] 中的漂亮数。漂亮数的定义是这个数能够整除它的每一位。 思路: 数位 dp , dp[i][j][k]: 前 i 为数字,最小共倍数为 j 时,这个数为 k 的个数。这里为什么要用到最小共倍数呢,因为一个数如果能够整除他的每一位,那么它一定能够整除他的每一位的最小共倍数。而且可以发现 lcm(2~9)=2520 ,由于这个数根本存不下,但我们看到这里是在 mod ,所以 2520 就够了,所以空间大小为 dp[19][50][2520] ,里面最多不超过 50 个最小共倍数。这里还有一个优化,我们每次计算这个数的时候都要乘以 10 ,其实这 dp[19][50][252] 已经够了。 代码: https://github.com/Mithril0rd/ComplexCode/blob/master/cf55d.cpp TAG: 数位 dp,[l,r] 中被每位整除 来源: https://www.cnblogs.com/Rojo/p/4066506.html

洛谷 P2657 (数位DP)

橙三吉。 提交于 2020-03-26 19:45:16
### 洛谷 P2657 题目链接 ### 题目大意: 给你一个数的范围 [A,B] ,问你这段区间内,有几个数满足如下条件: 1、两个相邻数位上的数的差值至少为 2 。 2、不包含前导零。 很简单的数位DP,可想只需标记前导零 lead, 前一个数 pre ,即可暴力统计答案,再记忆化就行了,但是有些地方还要细心一点。 比如在枚举到第一个有效位时(即非前导零),它当前只有一个数,而我们需要设 q = true (q 表示枚举到当前位时,是否满足条件,即相邻位之差是否达到 2 )。即我需要保证枚举到第二个有效数位时,要与第一个有效数位作差值比较的话,那么在枚举第一个有效位时,不能使得 q == false。 然后根据样例 1 可以知道,个位数也算。那么为了使第一位满足 abs(i - pre)>= 2 的话,那么我们需要使得一开始 pre == -1 即可,因为 i 最少会为 1 。 代码如下: 根据条件枚举数位 #include<iostream> #include<algorithm> #include<string.h> using namespace std; typedef long long ll; int A,B; int a[12],dp[12][12]; ll dfs(int pos,int pre,bool lead,bool limit){ if(pos==0

洛谷 P4999(数位DP)

六眼飞鱼酱① 提交于 2020-03-26 19:31:43
###洛谷 P4999 题目链接 ### 题目大意: 给你一个区间,求这段区间中所有数的,数位上的,数字之和。 分析: 这题与 洛谷 P2602 相似,稍微改一下就可以了。 求出 0 ~ 9 的个数,然后分别乘以 0 ~ 9 ,取模相加即可。要注意的是,在统计之和时,需要 加 mod 以保正答案正确,不然会 WA 两个点。 代码如下: #include<iostream> #include<algorithm> #include<string.h> using namespace std; typedef long long ll; const ll p = (ll) (1e9+7); ll n,m; int a[20],t; ll dp[20][20]; ll dfs(int pos,int t,int sum,bool lead,bool limit){ if(pos==0) return sum%p; if(!limit&&!lead&&dp[pos][sum]!=-1) return dp[pos][sum]%p; int up=limit?a[pos]:9; ll res=0; for(int i=0;i<=up;i++){ if(lead&&i==0) res=(res%p+dfs(pos-1,t,sum,true,limit&&i==a[pos])%p)%p;

洛谷 P4124 (数位 DP)

南笙酒味 提交于 2020-03-26 18:59:10
### 洛谷 P4124 题目链接 ### 题目大意: 给你一段区间,让你求满足下列两个条件时的数的个数。 1、至少有 3 个相邻相同数字 (即 111 、1111 、222 等) 2、不能同时出现 8 与 4 。 给定的区段 [L,R] 中 ,L 与 R 的数值范围为:10 10 ≤ L ≤ R < 10 11 分析: 1、用 q 与 w 标记 8 是否出现 ,4 是否出现。 2、为了得知是否有连续三个相等,故还需要 ppre 表示前两位中的第一位数位上的数,pre 表示前一位数位上的数,还需要再加 flag 标记是否当前已满足 ppre == pre == i 3、剩下的就是前导零与位数限制标记了 (即 lead 与 limit ) 故 dp 需要 5 维,pos、ppre、pre、flag、q、w ,然后直接记忆化即可。 代码如下: #include<iostream> #include<algorithm> #include<string.h> using namespace std; typedef long long ll; ll L,R; ll a[15]; ll dp[15][15][15][2][2][2]; ll dfs(int pos,int ppre,int pre,bool flag,bool q,bool w,bool lead,bool limit){

洛谷 P2602(数位DP)

房东的猫 提交于 2020-03-26 18:58:38
### 洛谷 P2602 题目链接 ### 题目大意: 给你一个区间,问你区间所有数字中,0、1、2 .... 9 的个数的总和分别为多少。 分析: 枚举 0 ~ 9 进行数位 DP 即可。 注意记忆化搜索: 必须要用到第二维来表示,前 1 ~ pos 位,某个数(0 ~ 9)的个数。 例如,我们在求这个区间中 2 的个数,直接看的话,后 pos 位 的 2 的个数好像与 1 ~ pos位 上有多少个 2 并无联系(在 !limit 情况下),那为什么还要开第二维呢? 实际算上来你会发现:比如当枚举到 222 这个数时,很显然 pos==0 后,2 的总个数(即 sum)为 3 。而如果我们枚举到 第 2 位 (十位上)时,用到的记忆化是 dp[2] ,它只记录的是后两位中,有 22 这个数,即两个 2 ,而实际我们求 222 时,应该使程序返回的是 sum== 3 。故我们需要记忆化 dp[2][1] ,让在前 1 ~ pos 位已有一个 2 的时候,返回 sum== 3 (即这个 1 最后会再加上后面两个 2 的个数 ,对应的是 22 ) 代码如下: #include<iostream> #include<algorithm> #include<string.h> using namespace std; typedef long long ll; ll n,m; int a

HDU 3555 数位DP

非 Y 不嫁゛ 提交于 2020-03-19 17:16:48
#include<cstdio> #include<iostream> #include<cstring> #include<cmath> using namespace std; #define LL long long //#define __int64 LL LL dp[22][3]; int pos[22]; //[][0] 包含49 //[][1] 最高位是9 但不含49 //[][2] 不含49 void initial() { dp[0][2]=1; for(int i = 1; i < 22; i++) { dp[i][0] = dp[i-1][0] * 10 + dp[i-1][1]; dp[i][1] = dp[i-1][2]; dp[i][2] = dp[i-1][2] * 10 - dp[i-1][1]; } } LL ans(LL a) { int i,cnt = 0; LL res = 0; memset(pos,0,sizeof(pos)); a++; while(a>10) { pos[cnt++] = a % 10; a /= 10; } pos[cnt] = a; bool flag = false; for(i = cnt; i >= 0; i--) { //cout<<dp[i][0]<<" "<<dp[i][1]<<" "<<dp[i][2

D. Beautiful numbers(数位DP)

a 夏天 提交于 2020-03-16 07:17:59
题目链接: https://codeforces.com/contest/55/problem/D 题目大意: 给你一个区间,让你求出这个区间里面有多少个数是满足这个数能被 他的所有位上的数 整除的。 具体思路: 对于能被他的所有位上的数整除这个条件,转换一下就是这个数能够整除他的所有位上的lcm。 但是这样的话还是需要求出这个数是谁。因为数的范围比较大,所以就应该考虑取模; 具体取模的时候,我们可以先算出1~9的lcm,这个是求的过程中的最大的lcm。我们在求这个数是什么的时候,就每次取模1~9的lcm就可以了。 但是这样的话,这个数组是要dp[19][2520+5][2520+5]这样的话,是会爆内存的。 然后内存还可以优化一下,这里的第三位表示的是当前的lcm 是多少,这一部分我们是可以预处理出来的,能够被2520整除的只有48个,也就说第三维我们是可以降到50的。 顺便还学到了一个数位DP的一个巨大的优化,就是对dp赋值的时候,我们没有必要每一次都对他清零。对于这个题,dp[i][j][k]表示前i位,当前的数是多大(mod2520),当前的位数lcm是多少。 也就是说这里并没有关于首位的标志(如果是开的四位的话,这样复杂度就会优化很小了)。 AC代码: 1 #include<bits/stdc++.h> 2 using namespace std; 3 # define