数位dp学习笔记

数位dp的标志:

  1. 要求统计满足一定条件的数的数量(即,最终目的为计数);
  2. 这些条件经过转化后可以使用「数位」的思想去理解和判断;
  3. 输入会提供一个数字区间(有时也只提供上界)来作为统计的限制;
  4. 上界很大(比如 1018),暴力枚举验证会超时。

数位dp的模板题 A - 不要62

实现非常的简单,设计一个dp[n][0/1] 选每一个数字的方案都记一下即可。

#include<bits/stdc++.h> using namespace std; int dp[15][2],vis[15][2]; int l,r,a[15]; int dfs(int len,int six,int lim){ if(len==0)return 1; if(!lim&&vis[len][six])return dp[len][six]; int res=0; for(int i=(lim?a[len]:9);i>=0;i--){ if(i==4)continue; if(six&&i==2)continue; res+=dfs(len-1,i==6,lim&&i==a[len]); } if(!lim){ vis[len][six]=1; dp[len][six]=res; } return res; } int solve(int x){ if(x==0)return 1; int len=0; memset(a,0,sizeof a); while(x){ a[++len]=x%10; x/=10; } return dfs(len,0,1); } int main(){ while(~scanf("%d%d",&l,&r)){ if(!l&&!r)break; printf("%d\n",solve(r)-solve(l-1)); } return 0; }

C - Round Numbers

这一道题记的状态有所改变,可以记 dp[len][x][y] 表示在到 len 这一个位置时,0的数量和1的数量分别是 xy 当然如果你想省时间的话就可以记 dp[len][x] 代表两者的差,注意差可能是负数,需要加上长度。

D - B-number

这道题记一下前面是否有 13 除 13 的余数转移即可。

E - Balanced Number

这一个题就很有意思了。需要你枚举每一个点,作为平衡点的情况,记一下两边力矩之差即可。

时间复杂度 O(len2200)

F - XHXJ's LIS

一道好题,代码不麻烦并且有一定的思维量。

首先复习 LIS 的求法,O(n2) 的求法很简单 , 但 O(nlogn) 的求法用 LIS 的特点 ,即LIS长度越长,他的最末的数越大。

而且我们发现这里的数码只有 10 个,那我们是否可以使用状压的方法,来压一下每一个数是否在LIS

中出现,在每一次转移时,往st里添加这个数(当然这一个步骤需要预处理) 即可。

G - Beautiful numbers

数感题,很容易发现,其实这个各个数位的积上限时很少的,好像只有 2520,所以就跟 D - B-number 这一题一样了,就只要最后的时候对答案检查一下即可。

因为kmod2520mods=kmods

I - 吉哥系列故事——恨7不成妻

数学推导题,推一下 sqsumsumcnt 的关系即可。

K - Matches Puzzle Game

把等式改为加式,记dp[x][b][c][flag] 表示剩余火柴,b,c 是否到顶,是否有进位。

这样的数位dp 不算很明显,需要进行一定的转化才可以变成一般的数位dp。

#include<cstdio> #include<cstring> #define int long long using namespace std; const int num[]= {6,2,5,5,4,5,6,3,7,6}; int n,mod,T; int dp[505][2][2][2]; bool vis[505][2][2][2]; int dfs(int x,bool b,bool c,bool flag) { if(x<0)return 0;//不能堆叠了 if(b&&c)return x==flag*2; if(vis[x][b][c][flag])return dp[x][b][c][flag]; int res=0; if(b) { for(int i=0; i<=9; i++) { res+=dfs(x-num[i]-num[(i+flag)%10],1,0,(flag+i)/10); res%=mod; if(i)res+=dfs(x-num[i]-num[(i+flag)%10],1,1,(flag+i)/10); res%=mod; } } else if(c) { for(int i=0; i<=9; i++) { res+=dfs(x-num[i]-num[(i+flag)%10],0,1,(flag+i)/10); res%=mod; if(i)res+=dfs(x-num[i]-num[(i+flag)%10],1,1,(flag+i)/10); res%=mod; } } else { for(int i=0; i<=9; i++) { for(int j=0; j<=9; j++) { int tmp=i+j+flag; res+=dfs(x-num[i]-num[tmp%10]-num[j],0,0,tmp/10); res%=mod; if(i)res+=dfs(x-num[i]-num[tmp%10]-num[j],1,0,tmp/10); res%=mod; if(j)res+=dfs(x-num[i]-num[tmp%10]-num[j],0,1,tmp/10); res%=mod; if(i&&j)res+=dfs(x-num[i]-num[tmp%10]-num[j],1,1,tmp/10); res%=mod; } } } vis[x][b][c][flag]=1; return dp[x][b][c][flag]=res; } //时间复杂度O(500*3*2*100) //分别表示在 x:剩余的木棍与 A需要的木棍的差 cnt: B C的暂停个数 (B C 等价) flag上一次有没有进位 //17 14 01+11=12 //细节 从小到大的堆放 signed main() { scanf("%lld",&T); int t=0; while(T--) { memset(vis,0,sizeof vis); memset(dp,0,sizeof dp); scanf("%lld%lld",&n,&mod); printf("Case #%lld: %lld\n",++t,dfs(n-3,0,0,0)); } return 0; }

对于数位dp 有三种求的量:最一般的就是求方案数,也有求和的,需要记一下每一个数的出现的个数,用这一个辅助数组进行最终的求值,当然还有求平方和的,这需要方案数,求和数组,共需要开三个数组才可以解决,详情可看I - 吉哥系列故事——恨7不成妻


__EOF__

本文作者火牛
本文链接https://www.cnblogs.com/hnczy/p/18706010.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   hnczy  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示