- 参考视频
强烈推荐
- 什么是数位dp
数位dp是一种特殊的动态规划,常用备忘录法(dfs+记录)来处理数位dp的问题。数位dp就是暴力的枚举整数的每一位,在枚举的过程中用dp[i][j]记录枚举过的可能,从而避免重复
- 数位dp能解决的问题
数位dp题型单一,一般来说数位dp是用于求解求取区间[l,r]中满足某个条件P(i)的数字的个数,l和r很大,一般为数十亿
。基本上看到上述条件就说明该题是数位dp
- 数位dp基本思路
数位dp的模板都是固定的,所有题目都可以用同一套模板,只需要改一下数字就可以了。为了方便起见,我们常用备忘录法处理数位dp。处理步骤如下
- 将区间
[l,r]
划分为[1,l-1]
和[1,r]
。此时求解[l,r]
中满足条件的数字个数就是求解[0,r]-[0,l-1]
- 从最高位开始枚举数字
i
的每一位,用采用深度优先遍历,将[0,r]
和[0,l-1]
中所有的数字列举出来,并记录满足条件的数字个数
- 记忆优化,在2中暴力枚举每一个数,在枚举的过程中有些组合是重复的,对于这些重复的组合我们可以使用dp来存储。
- 利用23求取
[1,r]
和[1,l-1]
直接相减得到最终结果
- 数位dp常见问题
- 数位dp一般情况下数据空间很大不能用数组存储状态,一般使用
嵌套哈希表
存储状态即map<int,map<int,int>>
- 对r和l-1进行枚举时,在枚举前要初始化状态
- 核心代码
数位dp的核心代码是dfs。dfs是通用的每个数位dp的问题都能用同一套dfs,只要改一下参数就好。
数位dp的状态一般是dfs中除了bool类型外的所有参数。具体情况,具体分析,但状态肯定都是dfs中的参数。
- 模板
P6218 [USACO06NOV] Round Numbers S
#include <iostream>
#include <algorithm>
#include <bits/stdc++.h>
#include <map>
#include <queue>
using namespace std;
int n;
int l;
int r;
vector<int> a;
vector<int> b;
map<int,map<int,map<int,int>>> dp;
void init(int x,vector<int> &m){
while(x!=0){
m.push_back(x%2);
x=x>>1;
}
}
int dfs(int t,int pos,int num0,int num1,bool flag,bool lead,vector<int> &roundNum){
if(pos<0){
if(num0>=num1) return 1;
else return 0;
}
if(!flag&&dp[pos][num0][num1]!=-1) return dp[pos][num0][num1];
int maxNum=0;
if(flag) maxNum=roundNum[pos];
else maxNum=1;
int ret=0;
for(int i=0;i<=maxNum;i++){
if(i==0){
if(lead){
ret+=dfs(t,pos-1,num0,num1,flag&&(i==maxNum),true,roundNum);
}else{
num0++;
ret+=dfs(t,pos-1,num0,num1,flag&&(i==maxNum),false,roundNum);
num0--;
}
}else{
num1++;
ret+=dfs(t,pos-1,num0,num1,flag&&(i==maxNum),false,roundNum);
num1--;
}
}
if(!flag){
dp[pos][num0][num1]=ret;
}
return ret;
}
void initDP(int m){
map<int,int> m1;
for(int num1=0;num1<=m;num1++){
m1.emplace(num1,-1);
}
map<int,map<int,int>> m2;
for(int num0=0;num0<=m;num0++){
m2.emplace(num0,m1);
}
for(int pos=0;pos<m;pos++){
dp.emplace(pos,m2);
}
}
int main(){
cin>>l>>r;
init(l-1,a);
init(r,b);
initDP(b.size());
int rRes= dfs(1,b.size()-1,0,0,true,true,b);
initDP(a.size());
int lRes= dfs(0,a.size()-1,0,0,true,true,a);
cout<<rRes-lRes<<endl;
return 0;
}