数位dp学习笔记
1 简介
适用题型
- 不关注数的大小;
- 经过转化后可以运用与数位有关的思想;
- 常见问法:在 中,有多少个数满足 。
方法
我们通常使用前缀和的思想和记忆化搜索来实现数位dp。
2 常见题型
模板
就是上面所说的常见问法。
例题:windy 数
int dfs(int k,int x,int p,int q)
{
if(!k)
return 1;
if(!p&&!q&&f[k][x]!=-1)
return f[k][x];
int res=0,y=q?a[k]:9;
for(int i=0;i<=y;i++)
{
if(abs(i-x)<2)
continue;
if(p&&!i) res+=dfs(k-1,-2,1,q&&(i==y));
else res+=dfs(k-1,i,0,q&&(i==y));
}
if(!p&&!q)
f[k][x]=res;
return res;
}
这道题的难点就在于对相邻两个数字之差至少为 的处理,我们一开始将 设为 ,后面只需要判断前导 即可。
单独判断左端点
这类题目的数据范围通常会很大, 和 需要用数组存,这时候就需要用 ,再单独判断 是否符合条件。
例题:Magic Numbers
ll dfs(int k,int x,int p)
{
if(k>len)
return x?0:1;
if(!p&&f[k][x]!=-1)
return f[k][x];
int y=p?a[k]:9;
ll res=0;
if(k&1)
{
for(int i=0;i<=y;i++)
{
if(i==d)
continue;
res=(res+dfs(k+1,(x*10+i)%m,p&&(i==y)))%mod;
}
}
else
{
if(d<=y)
res=(res+dfs(k+1,(x*10+d)%m,p&&(d==y)))%mod;
}
if(!p)
f[k][x]=res;
return res;
}
这道题就是多加了被 整除这个条件,我们在 dfs 中用 表示模 的余数,每次变为 ,最后判断是否为 即可。
bool check(char *s)
{
len=strlen(s+1);
int x=0;
for(int i=1;i<=len;i++)
{
int y=s[i]-'0';
x=(x*10+y)%m;
if(i&1)
{
if(y==d)
return false;
}
else
{
if(y!=d)
return false;
}
}
return !x;
}
这里顺便把 check 函数也放上来了。
统计数量以外的元素
这类题目通常问的不是符合条件的数的数量,而是问符合条件的数的和等,这时候用结构体进行记忆化即可。(这类题目对数学好的选手比较友好,因为通常需要推式子)
例题:恨 7 不成妻
node dfs(int k,int x,int y,int q)
{
if(!k)
return node{x&&y,0,0};
if(!q&&~f[k][x][y].u)
return f[k][x][y];
int z=q?a[k]:9;
node res={0,0,0},tmp;
for(int i=0;i<=z;i++)
{
if(i==7)
continue;
tmp=dfs(k-1,(x+i)%7,(y*10+i)%7,q&&(i==z));
res.u=(res.u+tmp.u)%p;
res.v=(res.v+tmp.v+i*v[k-1]%p*tmp.u%p)%p;
res.w=(res.w+tmp.w+i*i%p*v[k-1]%p*v[k-1]%p*tmp.u%p+i*v[k-1]%p*tmp.v%p*2%p)%p;
}
if(!q)
f[k][x][y]=res;
return res;
}
与状压结合
这类题目通常与数字出现的次数有关,这时候我们用 的二进制来进行相应的表示即可。
例题:Balanced Numbers
ll check(int x,int y)
{
for(int i=0;i<=9;i++)
{
if(y&(1<<i))
{
if((i&1)^(x&(1<<i))==0)
return 0;
}
}
return 1;
}
ll dfs(int k,int x,int y,int p,int q)
{
if(!k)
return check(x,y);
if(!p&&!q&&f[k][x][y]!=-1)
return f[k][x][y];
int z=q?a[k]:9,w;
ll res=0;
for(int i=0;i<=z;i++)
{
w=p&&!i;
res+=dfs(k-1,w?0:x^(1<<i),w?0:y|(1<<i),w,q&&(i==z));
}
if(!p&&!q)
f[k][x][y]=res;
return res;
}
注意表示出现次数的 用异或转移,表示是否出现过的 用或转移。
3 写在最后
根据个人经验,要想完全掌握数位dp,最重要的是多练习,这里推荐一份题单。
本文作者:Ginger_he
本文链接:https://www.cnblogs.com/Gingerhe/p/15847534.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步