这几周要从数论和dp中选择一个学习,由于队友选择了数论,我就选择了dp,刚开始要学习数位dp。
通过看了很多博客,知道了数位dp就是计数用的,最基础题型是计算一个区间(a,b)符合某个条件的数的个数。一般这种题就是枚举做,而数位dp就是一种暴力枚举的方法。它就是在数位上一位一位的枚举,使得这种枚举方式符合dp的定义,并且使用记忆化搜索,就会节省枚举的时间。
一般这种题都有模板。
int a[20];
int dp[20][state];
int dfs(int pos,int zhuangtai,/*这里可能有很多状态*/int limit)
{
if(pos==-1)return 1;//这里根据情况不一样,返回值不一样
if(!linit&&dp[pos][state]!=-1/*如果有前导零的题,这里还有前导零的判断*/)return dp[pos][state];
int sum=0;
int end=limit?a[pos]:9;//这里是根据这一位数是否应该有限制,设置枚举的最大数,不过这是十进制的
for(int i=0;i<=end;i++)//枚举循环
{
if()....//这里有许多限制条件。
sum+=dfs(pos-1,/*状态或者前导零*/limit&&i==a[pos])
}
if(!limit/*有时候有前导零*/)dp[pos][state];
return sum;
}
int solve(int x)
{
int pos=0;
while(x)
{
a[pos++]=x%10;
x/10;
}//这里是数位分解,一般十进制都是这样,
return dfs(pos-1,/*状态限制*/,1);
}
int main()
{
int q,w;
while(~scanf("%d%d",&q,&w))
{
printf("%d\n",solve(q)-solve(w));
}
return 0;
}
一般的限制条件有某个数中必须含有什么,或者能被什么整除什么的。整除的麻烦一点。
这个是比较简单的一个模板,如果需要添加限制条件,有两种方法,一种是在dfs()函数里的枚举里添加条件,另外一个是直接在数组dp【pos】【state】后面再加一维或者很多维,当然数据量要小才可以。
如果是条件很多就在状态限制里多加几条,如果是有权值的,比如 HDU 4734 ,这个就需要另外建一个函数另外算权值,其他的差不多,就是里面有一个博主说的 减法的艺术 是比较不错的。
还有就是有的要求不是十进制,是二进制的问题,这就把转换数位的地方变成二进制,并且把枚举的循环改成二进制就好了。
所以总结来看,数位dp的基本题型的简单题都可以套模板再加上变化一点就可以解决。
至于难题,目前还没有看过这种题解或者见过这种题。------(其实感觉做不了)