前面
最近学了点数位DP,也码了几个题,写点学习笔记
数位DP
数位DP就是在数位上进行DP,(个位,十位,百位之类的)
一般数位DP,都会让求在某一区间内满足某一种限定的数的数量。
DP的方式一般是DFS+记忆化。
状态的话就是
dp[pos][..]
d
p
[
p
o
s
]
[
.
.
]
,
pos
p
o
s
表示第pos位状态为
..
.
.
的满足题目限制的数量
对了…
一般我们在进行
dfs
d
f
s
之前要对枚举上界进行分解,就是把各个位分离,这样既容易记录状态,也容易判断枚举的范围。
看一下代码应该就理解的差不多了.
int solve(int x)
{
pos=0;//一定要记住啊每一次都要变成0,写题的时候老是忘
while(x)
{
a[pos++]=x%10;//把各个位分离开
x/=10;
}
return dfs(pos-1,....,true);
}
下面说一下
dfs
d
f
s
的时候对于上界的控制:
我们在
solve
s
o
l
v
e
里对枚举上界进行了拆分,
然后我们在
dfs
d
f
s
的时候设置一个
bool
b
o
o
l
变量
limit
l
i
m
i
t
来记录上一次
dfs
d
f
s
是否到达的上一个位的上界,
如果到达了上一个位的上界,那我们在这一次
dfs
d
f
s
的时候最大就只能枚举到这一位的上界
如果没有到达上一个位的上界,那么我们就可以一直枚举到
9
9
;
举个例子:
假设我们枚举的上界是,假如我们枚举第一位也就是百位是
6
6
的话,那我们第二位也就是十位就只能枚举到是不是,如果大于
7
7
的话就会超过枚举上界;相反,假如我们在枚举第一位的时候百位小于,那么我们第二位就可以在
0−9
0
−
9
之间任意枚举了。因为
5XX
5
X
X
显然小于
678
678
.
对于上界的控制还需要注意的就是在开始
dfs
d
f
s
,要把
limit
l
i
m
i
t
设置成为
true
t
r
u
e
,这样才能保证枚举一直不超过上界。
然后是对于前导
0
0
的处理:
和上界控制差不多,对于前导我们也是用一个
bool
b
o
o
l
变量
lead
l
e
a
d
来进行控制。
假若上一位是存在前导
0
0
且当前位又恰好选择了,那么
lead
l
e
a
d
位
true
t
r
u
e
;反之为
0
0
;
然后就是对于的讲解了:
//分别是枚举的位数,状态,是否到达枚举上界,是否有前导0
int dfs(int pos,int S,int limit,int lead)
{
if(pos==-1) //因为上面是从pos=0开始拆分的所以这个是搜索的结束
return 1;//对于返回:一般的题目中都是要对最后的状态进行判断,合法的话返回1,不合法的话返回0,做题多了也就了解套路了
if(dp[pos][S]==-1 && !limit && !lead) //对于没有特殊情况的话,直接上记忆化搜索
return dp[pos][S];
int up=limit ? a[pos] : 9;//这里是上界判断
int ans=0;//记录答案
for(int i=0;i<=up;i++)//开始枚举当前位所选的数字
{
........//一般会有一些判断
ans+=dfs(pos-1,S...,limit && i==up,lead && i==0);
//假若上一位到达了枚举上界且这一位也是枚举上界的话,那么下一位也必须是枚举上界,两个条件缺一不可。
//lead是用来判断前导0
}
if(!limit && !lead) //除去特殊情况,进行记忆化
dp[pos][S]=ans;
return ans; //返回
}
题目
学完数位
DP
D
P
之后也码了几道题,在我的博客里都有题解.(按照我做的顺序给出)
1.不要62 —————— 题解
2.HDU4734——————题解
3.POJ3252——————题解
4.HDU3709——————题解