数位dp 学习指南
来源:互联网 发布:ios socket多线程编程 编辑:程序博客网 时间:2024/05/21 04:43
什么是数位 DP
在信息学竞赛中,有一类难度不大但异常麻烦的问题——数位计数问题,这类问题的主要特点是询问的答案和一段连续的数的各个数位相关,并且需要对时间效率有一定要求。由于解决这类问题往往意味着巨大的代码量,而众多的特殊情况又意味着出现错误的巨大可能性,因此很少有人愿意解决此类问题,但只要掌握好的方法,解决这类问题也并非想象中的那样困难。
数位DP的解题思路
对于一个数,若其首位已经比询问上界小,则剩余位没有任何限制。此时如果能直接处理这一情况,则问题距离解决又会迈出一大步。 例如,在十进制下,计算[10000,54321]内的数字和,我们可以将其分解为: [10000,19999],[20000,29999],[30000,39999],[40000,49999],[50000,54321]。 前四个区间如果可以直接解决,则只需处理最后一个区间,进一步将最后一个区间划分为:[50000,50999],[51000,51999],[52000,52999],[53000,53999],[54000,54321]。同理将最后一个区间划分下去,最后可以得到以下区间划分: [10000,19999],[20000,29999],[30000,39999],[40000,49999], [50000,50999],[51000,51999],[52000,52999],[53000,53999], [54000,54099],[54100,54199],[54200,54299], [54300,54309],[54310,54319], [54320,54321]
数位DP的经典模板
该模板出处不详,但是此模板大大降低了数位dp类问题的难度。
typedef long long LL;const int maxn=22;int dig[maxn];LL f[maxn]/* [TODO] */;LL dfs(int pos,/* TODO */,int limit){ if (pos<0) return /* TODO */; if (!limit&&f[pos]/* [TODO] */!=-1) return f[pos]/* [TODO] */; LL res=0; int last=limit?dig[pos]:9; for (int i=0;i<=last;i++){ res+=dfs(pos-1,/* TODO */,limit&&(i==last)); } if (!limit) f[pos]/* [TODO] */=res; return res;}LL solve(LL n){ int len=0; while (n){ dig[len++]=n%10; n/=10; } return dfs(len-1,/* TODO */,1);}
经典题目
Hdu 2089 不要62
求给定区间中不含有62和4的数的个数。
LL dfs(int pos,int pre,int fg,int limit){ if (pos<0) return fg==0; if (!limit&&f[pos][pre][fg]!=-1) return f[pos][pre][fg]; LL res=0; int last=limit?dig[pos]:9; for (int i=0;i<=last;i++){ res+=dfs(pos-1,i,fg||((pre==6)&&(i==2))||(i==4),limit&&(i==last)); } if (!limit) f[pos][pre][fg]=res; return res;}
Hdu 3555 Bomb
求给定区间的含有49的数的个数。
LL dfs(int pos,int pre,int istrue,int limit){ if (pos<0) return istrue; if (!limit && f[pos][pre][istrue]!=-1) return f[pos][pre][istrue]; int last=limit?dig[pos]:9; LL ret=0; for (int i=0;i<=last;i++){ int ok=(pre==4)&&(i==9); ret+=dfs(pos-1,i,istrue||ok,limit&&(i==last)); } if (!limit) f[pos][pre][istrue]=ret; return ret;}
windy数
求给定区间范围内的,求相邻数位之差绝对值不小于2的数的个数。
LL dfs(int pos,int pre,int fg,int limit){ if (pos<0) return 1; if (!limit && f[pos][pre][fg]!=-1) return f[pos][pre][fg]; int last=limit?dig[pos]:9; LL ret=0; for (int i=0;i<=last;i++){ if (fg==0||abs(i-pre)>=2) ret+=dfs(pos-1,i,fg||i,limit&&(i==last)); } if (!limit) f[pos][pre][fg]=ret; return ret;}
Hdu 3709 Balanced Number
平衡数。数n以数n中的某个位为支点,每个位上的数权值为(数字xi*(posi - 支点的posi)),如果数n里有一个支点使得所有数权值之和为0那么她就是平衡数。比如4139,以3为支点,左边 = 4 * (4 - 2) + 1 * (3 - 2) = 9,右边 = 9 * (1 - 2) = -9,左边加右边为0,所以4139是平衡数。现在给出一个区间[l,r],问区间内平衡数有多少个?
LL dfs(int pos,int o,int pre,int limit){ LL res=0; if (pos<0) return pre==0; if (!limit&&f[pos][o][pre]!=-1) return f[pos][o][pre]; int last=limit?dig[pos]:9; for (int i=0;i<=last;i++){ res+=dfs(pos-1,o,pre+i*(pos-o),limit&&(i==last)); } if (!limit) f[pos][o][pre]=res; return res;}LL solve(LL n){ if (n<0) return 0; if (n==0) return 1; int len=0; while (n){ dig[len++]=n%10; n/=10; } LL ans=0; for (int i=0;i<len;i++){ ans+=dfs(len-1,i,0,1); } ans=ans-len+1; return ans;}
CodeForces 55D Beautiful numbers
如果一个数能够被其每个数位的数都整除,那么这个数就叫做美丽数。
int check(int bit,int mod){ for (int i=2;i<=9;i++){ if (bit&(1<<(i-2))){ if (mod%i) return 0; } } return 1;}LL dfs(int pos,int bit,int mod,int limit){ if (pos<0) return check(bit,mod); if (!limit && f[pos][bit][mod]!=-1) return f[pos][bit][mod]; int last=limit?dig[pos]:9; LL ret=0; for (int i=0;i<=last;i++){ int nbit=bit; if (i>=2) nbit|=1<<(i-2); ret+=dfs(pos-1,nbit,(mod*10+i)%MOD,limit&&(i==last)); } if (!limit) f[pos][bit][mod]=ret; return ret;}
Hdu 3652 B-number
求小于n是13的倍数且含有'13'的数的个数。
LL dfs(int pos,int pre,int fg,int md,int limit){ if (pos<0) return fg&&(md==0); if (!limit&&f[pos][pre][fg][md]!=-1) return f[pos][pre][fg][md]; LL res=0; int last=limit?dig[pos]:9; for (int i=0;i<=last;i++){ res+=dfs(pos-1,i,fg||(pre==1&&i==3),(md*10+i)%13,limit&&(i==last)); } if (!limit) f[pos][pre][fg][md]=res; return res;}
Hdu 4352 XHXJ's LIS
求[L,R]内最长递增子序列是k的数的个数;
LL f[maxn][1<<10][11];int K;int dig[maxn];int getLIS(int bit){ int res=0; for (int i=0;i<10;i++){ if (bit&(1<<i)) res++; } return res;}int gaoBit(int bit,int d){ if (bit&(1<<d)) return bit; if ((1<<d)>bit) return bit|(1<<d); bit|=(1<<d); for (int i=d+1;i<10;i++){ if (bit&(1<<i)) return bit^(1<<i); } return 0;}LL dfs(int pos,int bit,int limit){ if (pos<0) return getLIS(bit)==K; if (!limit&&f[pos][bit][K]!=-1) return f[pos][bit][K]; LL res=0; int last=limit?dig[pos]:9; for (int i=0;i<=last;i++){ int go; if (bit==0&&i==0) go=0; else go=gaoBit(bit,i); res+=dfs(pos-1,go,limit&&(last==i)); } if (!limit) f[pos][bit][K]=res; return res;}LL solve(LL n){ int len=0; while (n){ dig[len++]=n%10; n/=10; } return dfs(len-1,0,1);}
HDU 4507 吉哥系列故事——恨7不成妻
中文题。需要推公式。
#include <iostream>#include <cstring>using namespace std;typedef long long LL;const int maxn=22;const int MOD=1e9+7;typedef pair<LL,LL> PII;typedef pair<PII,LL> PIII;inline LL fst(PIII a){ return a.first.first;}inline LL sec(PIII a){ return a.first.second;}inline LL thd(PIII a){ return a.second;}inline PIII makeZeroPIII(){ return make_pair(make_pair(0,0),0);}inline PIII makeOnePIII(){ return make_pair(make_pair(1,0),0);}LL power[maxn];PIII f[maxn][11][8];bool v[maxn][11][8];int dig[maxn];PIII gao(PIII a, PIII b,int i,int pos){ PIII res=makeZeroPIII(); res.first.first=(fst(a)+fst(b))%MOD;//数量 res.first.second=(sec(a)+sec(b)+((i*power[pos])%MOD*fst(b))%MOD)%MOD;//和 res.second=(thd(a)+thd(b)+((2*i*power[pos])%MOD*sec(b))%MOD+(((i*i*power[pos])%MOD*power[pos])%MOD*fst(b))%MOD)%MOD;//平方和 return res;}PIII dfs(int pos,int pre,int sev,int limit){ if (pos<0) { if (pre!=0&&sev!=0) return makeOnePIII(); else return makeZeroPIII(); } if (!limit&&v[pos][pre][sev]) return f[pos][pre][sev]; PIII res=makeZeroPIII(); int last=limit?dig[pos]:9; for (int i=0;i<=last;i++){ if (i==7) continue; PIII tmp=dfs(pos-1,(pre*10+i)%7,(sev+i)%7,limit&&(i==last)); res=gao(res,tmp,i,pos); } if (!limit){ v[pos][pre][sev]=true; f[pos][pre][sev]=res; } return res;}LL solve(LL n){ int len=0; while (n){ dig[len++]=n%10; n/=10; } PIII ans = dfs(len-1,0,0,1); return thd(ans);}int main(){ memset(v,0,sizeof(v)); memset(power,0,sizeof(power)); power[0]=1; for (int i=1;i<maxn;i++){ power[i]=(power[i-1]*10)%MOD; } int T; cin>>T; while (T--){ LL a,b; cin>>a>>b; cout<<(solve(b)-solve(a-1)+MOD)%MOD<<endl; } return 0;}
2 0
- 数位dp 学习指南
- 数位dp
- 数位DP
- 数位DP
- 数位dp
- 数位dp
- 数位dp
- 数位DP
- 数位dp
- 数位DP
- 【数位DP】
- 数位DP
- 数位dp
- 数位dp
- 数位DP
- 数位DP
- 数位dp
- 数位DP
- android 源码下载操作
- poj3613Cow Relays 矩阵快速幂
- 对众测平台的深度分析
- mysql提示ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)
- 支持向量机通俗导论(理解SVM的三层境界)
- 数位dp 学习指南
- HYSBZ 1406 密码箱
- 用mkfs.jffs2 命令制作jffs2镜像文件
- CentOS虚拟机安装SAMBA服务器 实现与Windows宿主机文件共享
- python软件工程师面试题
- 查找两个表同样属性列下更新了的内容
- KVZ "KOF V.S. Zombies" Demo
- java序列化之后,对象的引用关系?
- java大整数模板