【学时总结】 ◆学时·IV◆ 数位DP
【学时·IV】 数位DP
■基本策略■
说白了就是超时和不超时的区别 ;)
有一些特别的题与数位有关,但是用一般的枚举算法会超时。这时候就有人提出了——我们可以用动态规划!通过数字前一位和后一位之间的关系,逐渐推导出所有数位上的值作为初始化(也有些不是),实现大部分计数问题的高效解决。
主要题型大概就是求 Min~Max 之间满足条件 E() 的数的个数,这里使用了前缀和的思想,即 F[i~j]=F[0~ j]-F[0~(i-1)]。一般是将 F[] 初始化,但是针对某些特别题型,比如 E() 针对不同的数据不同,这时候需要对每一个数据单独求解。
■一般的计数问题■
在十进制里做DP ♪(´▽`)
◆入门练手◆ 不要62
- 【HDU - 2089】
- 【解析】
这是一道基本上称得上“版题”的基础题。先给出m,n,也就是 Min,Max 。这里的 E(n) 则是 n不含4或62。
先给出一个最基本的结论:
若A < B,则A、B的十进制表示 {an...a2a1a0}、{bn...b2b1b0}必存在 ar=br(k < r < n)且 ak < bk
接下来是动态规划的初始化——令F[i][j]表示第i位是j的数的方案总数。当满足第i位(j)不为4,第i-1位(k)不为2或者i-1位为2但是第i位不为6:
若当前枚举到第i位,第i位数字为j,第i-1位数字为k
当且仅当j≠4且k≠2或者k=2但j≠6时,有dp[i][j]=sum(dp[i-1][k])
通过三层从小到大的循环完成i,j,k的枚举。
这是我们继续建立计算的基础——先用dig[]储存m、n的十进制表示,然后从高位开始枚举0~dig[i]-1,保证枚举出的数小于给出的数,且。设当前枚举位为j,如果满足 E() ,就在答案中加上 F[i][j]。
- 【源代码】
/*Lucky_Glass*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int F[15][15];
void GetDig(int num,int dig[])
{do{dig[++dig[0]]=num%10;num/=10;}while(num);
}
void GetF()
{F[0][0]=1;for(int i=1;i<=7;i++)for(int j=0;j<10;j++)for(int k=0;k<10;k++)if((j!=4) && !(j==6 && k==2))F[i][j]+=F[i-1][k];
}
int GetAns(int num,int dig[])
{int ret=0;for(int i=dig[0];i>0;i--){for(int j=0;j<dig[i];j++){if((j!=4) && !(j==2 && dig[i+1]==6))ret+=F[i][j];}if((dig[i]==4) || (dig[i]==2 && dig[i+1]==6))break;}return ret;
}
int main()
{GetF();int m,n;while(~scanf("%d%d",&n,&m) && n && m){int dign[15]={},digm[15]={};GetDig(n,dign);GetDig(m+1,digm);int ansn=GetAns(n,dign),ansm=GetAns(m+1,digm);printf("%d\n",ansm-ansn);}return 0;
}
◆有点意思◆ B-number
这道题做不做的出来,你心里难道就没有一点B数吗?
- HDU - 3652
- 【解析】
每个人心中都有一个B数(向yhn大佬致敬)。这道题其实是上一个题的升级版。
万事开头难,先看看状态定义:
dp[i][j][k] (满足0 ≤ i < len,0 ≤ j < 13,0 ≤ k < 3)
表示第i位时当前数模13余j数中,满足条件E(k)的数的个数;
E(k):k=0时,不存在13;k=1时,不存在13但是存在3;k=2时,不存在1或3
这里的第三维其实也可以用k表示当前数位的数字,但是显然可以更加优化!有时候只需要储存一些会对答案产生影响的条件作为一维。虽然减少了空间消耗,但是作为代价,思维的复杂程度和转移方程的错误率也相应增加,初学者还是不要尝试这种方法。(`・ω・´)
由于这道题有多组数据(o(゚Д゚)っ!),而且每一组数据相应的dp数组值不一样。这就意味着要针对每一组数据单独进行一次DP求解——这是数位DP中较特殊的题型。
与一般数位DP相同,我们仍需考虑n的数位。但是因为题目默认是1~n,就没有必要进行前缀和的操作了,我们可以同时考虑数位和DP转移,但是DP参数就会更复杂:
ll DP(int pos,int mod,int lst,bool lim,bool flg) //你没有看错,是long long /* pos: 现在枚举到的位置 mod: 当前数模13的余数 lst: 上一个数位的数字 lim: 是否达到数位限制* flg: 当前是否已经存在13 */
*:
什么叫数位限制?
我们令n的10进制表示为dig[0~len-1]、枚举的数字m的10进制表示为fdig[0~len-1],若我们现在从最高位枚举到第k位,满足dig[r]==fdig[r](k < r < len),则称第k数位的枚举受数位限制,此时的fdig[k]只能取 0~dig[k];如果不受数位限制,则fdig[k]可取0~9!
总而言之,有了数位限制才能控制枚举出的数是小于等于n的数!
特别声明:数的最高位总受数位限制
于是我们发现,dp[][][] 只能储存当前位不受数位限制的答案,所以当前位受限制时不能将答案储存在dp里,同样,在记忆化搜索的记忆性返回的时候要保证现在不受数位限制。
接下来就是转移方程了:
令tot=sum(dp[pos-1][(mod*10+1)%mod][]),第三维由记忆化的返回值决定;
当flg==true时,dp[pos][mod][0]=tot;
当flag==false且lst==1时,dp[pos][mod][1]=tot;
当flag==false且lst!=1时,dp[pos][mod][2]=tot;
我知道看上面的这么多内容很难理解,所以下面放代码啦。
- 【源代码】
/*Lucky_Glass*/
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
#define MOD 13
ll dp[40][13][3];
//[i][j][k]:k=2 - no 1 or 3; k=1 - no 13 but 3; k=0 no 13
int num[15];
ll DP(int pos,int mod,int lst,bool lim,bool flg)
{if(pos<0){if(flg && !mod) return 1ll;else return 0ll;}if(dp[pos][mod][0]!=-1 && !lim && flg) return dp[pos][mod][0];if(dp[pos][mod][1]!=-1 && !lim && lst==1 && !flg) return dp[pos][mod][1];if(dp[pos][mod][2]!=-1 && !lim && lst!=1 && !flg) return dp[pos][mod][2];int limit=lim? num[pos]:9;ll tot=0;for(int i=0;i<=limit;i++){bool lflg=(flg||(lst==1 && i==3)),llim=(lim && (i==limit));tot+=DP(pos-1,(mod*10+i)%MOD,i,llim,lflg);}if(!lim){if(flg) dp[pos][mod][0]=tot;if(!flg && lst==1) dp[pos][mod][1]=tot;if(!flg && lst!=1) dp[pos][mod][2]=tot;}return tot;
}
ll GetAns(int n)
{int len=0;memset(num,0,sizeof num);while(n)num[len++]=n%10,n/=10;return DP(len-1,0,0,true,false);
}
int main()
{memset(dp,-1,sizeof dp);int n;while(~scanf("%d",&n))printf("%lld\n",GetAns(n));return 0;
}
■异进制的世界■
◆预备烧脑◆ Amount of Degrees
- URAL - 1057
- 【解析】
其实就是求区间[X,Y]中有多少个数能表示成一个B进制的01数串,并且1的个数恰好为K(比如 B=3,K=2 时 101(3)=3^2+3^0=10(10))。
求区间中的个数仍然是使用前缀和的思想。
但是由于B进制并不方便思考,所以将B进制强制转换为2进制(重点:将除10进制外的所有进制按照2进制强制处理),这也是为什么题目给的Y是以2的幂为上限(Y≤2^31-1)。
定义 F[i][j] 为满足条件的长度为i,包含j个1的二进制数。容易得到转移方程:
F[i][j]=F[i-1][j]+F[i-1][j-1]
之后就是处理答案。也就是GetAns(),其实和dig[]的思路一样,只是因为用2进制储存,还要比十进制更简单——只需要判断当前位是0还是1就可以了。
- 【源代码】
/*Lucky_Glass*/
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int F[40][40];
void init()
{F[0][0]=1;for(int i=1;i<40;i++){F[i][0]=F[i-1][0];for(int j=1;j<=i;j++)F[i][j]=F[i-1][j]+F[i-1][j-1];}
}
int GetAns(int num,int mul,int mod)
{int sit[40]={},siz=1,ret=0;while(num) sit[siz++]=num%mod,num/=mod;for(int i=siz-1;i>0 && mul>=0;i--){if(sit[i]>1) {ret+=F[i-1][mul-1]+F[i-1][mul];break;}if(sit[i]==1) ret+=F[i-1][mul],mul--;}return ret;
}
int main()
{init();int x,y,k,b;scanf("%d%d%d%d",&x,&y,&k,&b);int ans1=GetAns(y+1,k,b),ans2=GetAns(x,k,b);printf("%d\n",ans1-ans2);return 0;
}
The End
Thanks for reading!
-Lucky_Glass
转载于:https://www.cnblogs.com/LuckyGlass-blog/p/9121151.html
【学时总结】 ◆学时·IV◆ 数位DP相关推荐
- 【校内模拟】A(容斥原理)(数位DP)(范德蒙德恒等式)(高精度)
简要题意: 请你对满足下列条件的正整数序列 A1,A2⋯AnA_1,A_2\cdots A_nA1,A2⋯An 进行计数. ∀1≤i≤n,Li≤Ai≤Ri\forall 1\leq i\leq ...
- 【bzoj 1833】【codevs 1359】 [ZJOI2010]count 数字计数(数位dp)
1833: [ZJOI2010]count 数字计数 Time Limit: 3 Sec Memory Limit: 64 MB Submit: 2774 Solved: 1230 [Submit ...
- 不要62 ---数位DP
题意:求m到n中不含62和4的数的个数. 题目链接 思路:数位dp模板求满足的数字或不满足的数字,刚学,就求不满足的数. #include<stdio.h> #include<str ...
- bzoj 3598 [ Scoi 2014 ] 方伯伯的商场之旅 ——数位DP
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3598 数位DP...东看西看:http://www.cnblogs.com/Artanis/ ...
- cojs 简单的数位DP 题解报告
首先这道题真的是个数位DP 我们考虑所有的限制: 首先第六个限制和第二个限制是重复的,保留第二个限制即可 第五个限制在转移中可以判断,不用放在状态里 对于第一个限制,我们可以增加一维表示余数即可 对于 ...
- 数位DP 不断学习中。。。。
1, HDU 2089 不要62 :http://acm.hdu.edu.cn/showproblem.php?pid=2089 题意:不能出现4,或者相邻的62, dp[i][0],表示不存在不吉 ...
- [数位dp] spoj 10738 Ra-One Numbers
题意:给定x.y.为[x,y]之间有多少个数的偶数位和减去奇数位和等于一. 个位是第一位. 样例: 10=1-0=1 所以10是这种数 思路:数位dp[i][sum][ok] i位和为sum 是否含有 ...
- 数位dp(求1-n中数字1出现的个数)
题意:求1-n的n个数字中1出现的个数. 解法:数位dp,dp[pre][now][equa] 记录着第pre位为now,equa表示前边是否有降数字(即后边可不能够任意取,true为没降,true为 ...
- 数位dp ---- 2020 icpc 上海 Sum of Log(枚举高位的二进制数位dp)
题目链接 题目大意 : 解题思路: 这里有个很核心的地方就是log2(i+j)\text{log2(i+j)}log2(i+j)本质上就是看看i+j\text{i+j}i+j的二进制高位在哪里? 那么 ...
- 数位dp ---- 暴力 + 二进制的数位dp 2020济南 L Bit Sequence
题目链接 题目大意 f(x)=x的二进制中1的个数f(x)=x的二进制中1的个数f(x)=x的二进制中1的个数 给你一个数组[a1,a2,a3...,am]m∈[1,100][a_1,a_2,a_3. ...
最新文章
- node.js学习5--------------------- 返回html内容给浏览器
- golang实践LSM相关内容
- POJ 2516 基础费用流
- java 中sun.net.ftp_开发FTP不要使用sun.net.ftp.ftpClient
- WEB安全基础-SQL注入演示
- iOS与H5交互(WKWbebView)
- 呕心码出的代码无人看,做个优秀的技术人好难
- 如何将一个JavaScript数组打乱顺序?
- 逻辑电路 -异或门Xor Gate
- java 算法之快速排序
- MATLAB 50行代码绘制超好看的旋转九边形
- 字节跳动员工基本年薪曝光:最高 254 万元
- 期货软件开发-期货软件搭建平台
- 德州大学达拉斯分校计算机专业博士,德克萨斯大学达拉斯分校计算机科学理科硕士入学条件及实习就业...
- Oleg Shilo:基于CS-Script的Notepad++的插件_.Net技术
- TX2超详细,超实用刷机教程(亲测有效,所有步骤都是博主亲自实践过)
- 牛客网华为机试【汽水瓶】
- ffmpeg简易使用应用分享(m3u8下载与视频合并等)
- Toad for Oracle 使用说明
- oracle 字符串去重