打了几个数位$dp$,发现自己除了会打模板之外没有任何长进,遇到非模板题依然什么都不会

那么接下来这篇文章将介绍如何打模板(滑稽)

假设我们要处理$l----r$

采用记忆化搜索的方式,枚举$<=r$每一种情况,枚举每一位,然后再枚举$<=l-1$每一种情况,然后两个值相减即可

我们可以比较轻松打出一个模板

ll dfs(ll x,ll pre,ll lead,ll limit){
if(x>pos) return 1;
if(!limit&&f[x][pre]) return f[x][pre];
ll ans=0;
ll mx=limit?maxn[pos-x+1]:9;
for(ll i=0;i<=mx;i++){
if(????????) continue;
if(lead&&i==0) ans+=dfs(x+1,-2,1,limit&&(i==mx));
else ans+=dfs(x+1,i,0,limit&&(i==mx));
}
if(!limit&&!lead) f[x][pre]=ans;
return ans;
}

解释一下$limit$是什么

假设有一个数

$1\   2\  3\  4\  5\  6$

$1\   2\  3\  ?\  ?\  ?$

我们枚举到第四位时最多枚举到$4$,

$1\   2\  3\  4\  5\  6$

$1\   2\  2\  ?\  ?\  ?$

这时我们枚举到第四位最多枚举到$9$

$limit$就是判断这个的

那么为什么要在$!limit$下才记忆化呢?

如果在所有情况下我们都记录f,那么假如之前枚举到$9$时你记录了一个答案,然后当前位有$limit$限制根本枚举不到$9$,你仍然用了这个f会出现错误

当然你这样记忆化也可以这样避免冲突

ll dfs(ll x,ll limit,ll tmp,ll d){
if(f[x][limit][tmp][d])
return f[x][limit][tmp][d];
………………
f[x][limit][tmp][d]=ans;
return ans;
}

解释一下$lead$

处理前导0用的,

有的时候

$0\   0\  0\  4\  5\  6$

也被认为是合法的,这时处理可能会出现问题,$lead$特判特殊处理

那么我们看几道模板题

例题

windy数

Windy 定义了一种 Windy 数:不含前导零且相邻两个数字之差至少为2的正整数被称为 Windy 数。

Windy 想知道,在l和r 之间,包括l 和 r ,总共有多少个 Windy 数?

非常简单对不对

套模板

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define A 50
ll f[A][A],maxn[A];
ll pos=0,a,b;
ll dfs(ll x,ll pre,ll lead,ll limit){
if(x>pos) return 1;
if(!limit&&f[x][pre]) return f[x][pre];
ll ans=0;
ll mx=limit?maxn[pos-x+1]:9;
// printf("mx=%lld\n",mx);
for(ll i=0;i<=mx;i++){
if(abs(pre-i)<2) continue;
if(lead&&i==0) ans+=dfs(x+1,-2,1,limit&&(i==mx));
else ans+=dfs(x+1,i,0,limit&&(i==mx));
// printf("ans=%lld\n",ans);
}
if(!limit&&!lead) f[x][pre]=ans;
return ans;
}
ll solve(ll x){
pos=0;
memset(f,0,sizeof(f));
while(x){
maxn[++pos]=x%10;
x/=10;
}
ll ans=dfs(1,-2,1,1);
return ans;
}
int main(){
scanf("%lld%lld",&a,&b);
swap(a,b);
printf("%lld\n",solve(a)-solve(b-1));
}

不要62

$l---r$间数位上不含$4$且没有$62$相连的数个数

套模板

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define A 50
ll f[A][A],maxn[A];
ll pos=0,a,b;
ll dfs(ll x,ll pre,ll limit){
if(!x) return 1;
if(!limit&&f[x][pre]) return f[x][pre];
// printf("x=%lld pre=%lld limit=%lld\n",x,pre,limit);
ll ans=0;
ll mx=limit?maxn[x]:9;
for(ll i=0;i<=mx;i++){
if(pre==6&&i==2) continue;
if(i==4) continue;
ans+=dfs(x-1,i,limit&&(i==mx));
}
// printf("ans=%lld\n",ans);
if(!limit) f[x][pre]=ans;
return ans;
}
ll solve(ll x){
pos=0;
memset(f,0,sizeof(f));
while(x){
maxn[++pos]=x%10;
x/=10;
}
ll ans=dfs(pos,0,1);
return ans;
}
int main(){
a=233,b=233;
while(a!=0&&b!=0){
scanf("%lld%lld",&a,&b);
if(a<b)swap(a,b);
if(a==0||b==0){
return 0;
}
printf("%lld\n",solve(a)-solve(b-1));
}
}

手机号码

至少$3$个相邻的相同的数,$8$ $4$不能同时出现

套模板,注意下特判,不然会70到自闭

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define A 30
ll f[A][A][A][2][2][2],pos[A];
ll tot=0,a,b;
ll dfs(ll x,ll prer,ll pre,ll limit,ll _4,ll _8,ll ok){
if(_4&&_8) return 0;
if(x>tot&&!ok) return 0;
if(x>tot&&ok) return 1;
if(!limit&&f[x][prer][pre][_4][_8][ok]) return f[x][prer][pre][_4][_8][ok];
ll maxn=limit?pos[tot-x+1]:9;
ll ans=0;
for(ll i=0;i<=maxn;i++){
if(x==1&&i==0) continue;
if(_4&&i==8) continue;
if(_8&&i==4) continue;
if(pre==prer&&i==pre&&i==prer)
ans+=dfs(x+1,pre,i,(limit&&i==maxn),(_4||i==4),(_8||i==8),1);
else
ans+=dfs(x+1,pre,i,limit&&(i==maxn),(_4||i==4),(_8||i==8),ok);
// printf("ans=%lld i=%lld maxn=%lld\n",ans,i,maxn);
}
if(!limit)
f[x][prer][pre][_4][_8][ok]=ans;
return ans;
}
ll solve(ll x){
tot=0;
if(x<1e10) return 0;
memset(f,0,sizeof(f));
while(x){
pos[++tot]=x%10;
x/=10;
}
ll ans=dfs(1,0,0,1,0,0,0);
return ans;
}
int main(){
scanf("%lld%lld",&a,&b);
if(a<b) swap(a,b);
printf("%lld\n",solve(a)-solve(b-1));
}

花神的数论题

设 sum(i) 表示 i 的二进制表示中 1 的个数。给出一个正整数 N ,花神要问你派(Sum(i)),也就是 sum(1)—sum(N) 的乘积。

套模板,等等,我要套什么,

这个题不算很板,值得思考思考。

数位dp计算出所有二进制下sum个数,然后快速幂处理一下,

代码

#include<bits/stdc++.h>
using namespace std;
#define A 53
#define mod 10000007
#define ll long long
ll n,tot=0,sum=0;
ll f[A][2][A][A],ans[A],pos[A];
ll dfs(ll cur,ll up,ll tmp,ll d){
if(!cur)
return tmp==d;
if(~f[cur][up][tmp][d])
return f[cur][up][tmp][d];
ll lim=up?pos[cur]:1;
ll ret=0;
for(ll i=0;i<=lim;i++)
ret+=dfs(cur-1,up&&i==lim,tmp+(i==1),d);
return f[cur][up][tmp][d]=ret;
}
ll meng(ll a,ll b){
ll ret=1;
while(b)
ret=ret*(b&1?a:1)%mod,a=a*a%mod,b>>=1;
return ret;
}
ll solve(ll x){
sum=1;
while(x){
pos[++tot]=x&1;
x>>=1;
}
for(ll i=1;i<=tot;i++){
memset(f,-1,sizeof(f));
ans[i]=dfs(tot,1,0,i);
}
for(ll i=1;i<=tot;i++){
sum=(sum*max(meng(i,ans[i]),1ll))%mod;
}
return sum;
}
int main(){
scanf("%lld",&n);
printf("%lld\n",solve(n));
}

剩下一些题都不算很板

数数

淘金

方伯伯商场之旅

题解慢慢补充八

那么你现在会模板了吗?

数位dp从会打模板到不会打模板的更多相关文章

  1. 数位dp模板 [dp][数位dp]

    现在才想到要学数位dp,我是不是很弱 答案是肯定的 以一道自己瞎掰的题为模板 //题: //输入数字n //从0枚举到n,计算这n+1个数中含有两位数a的数的个数 //如12930含有两位数93 #i ...

  2. [模板] 数位dp

    数位dp 简介 数位dp指满足特定性质的数的计数, 如求 \([l, r]\) 区间内不含 \(2\) 的数的个数. 一般来说, 数位dp利用dfs解决, 有时状态数较多, 需要hash表优化. 模板 ...

  3. 51nod 1009 数字1的数量(数位dp模板)

    给定一个十进制正整数N,写下从1开始,到N的所有正数,计算出其中出现所有1的个数. 例如:n = 12,包含了5个1.1,10,12共包含3个1,11包含2个1,总共5个1.   数位dp的模板题   ...

  4. 数位dp讲解及模板

    转载自:传送门 数位DP其实是很灵活的,所以一定不要奢求一篇文章就会遍所有数位DP的题,这一篇只能是讲清楚一种情况,其他情况遇到再总结,在不断总结中慢慢体会这个思想,以后说不定就能达到一看到题目就能灵 ...

  5. 51nod 1009 - 数字1的数量 - [数位DP][模板的应用以及解释]

    题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1009 基准时间限制:1 秒 空间限制:131072 KB 给 ...

  6. HDU 2089 不要62(数位dp模板题)

    http://acm.hdu.edu.cn/showproblem.php?pid=2089 题意:求区间内不包含4和连续62的数的个数. 思路: 简单的数位dp模板题.给大家推荐一个好的讲解博客.h ...

  7. POJ 3286 How many 0's(数位DP模板)

    题目链接:http://poj.org/problem?id=3286 题目大意: 输入n,m,求[n,m]的所有数字中,0出现的总数是多少,前导零不算. 解题思路: 模板题,设dp[pos][num ...

  8. 数位dp小结以及模板

    这里是网址 别人的高一啊QAQ.... 嗯一般记忆化搜索是比递推好写的所以我写的都是dfs嗯......(因为我找不到规律啊摔,还是太菜.....) 显然这个东西的条件是非常的有套路..但是不管怎么样 ...

  9. 数位dp总结 之 从入门到模板

    转发自WUST_WenHao巨巨的博客 基础篇 数位dp是一种计数用的dp,一般就是要统计一个区间[le,ri]内满足一些条件数的个数.所谓数位dp,字面意思就是在数位上进行dp咯.数位还算是比较好听 ...

  10. 数位dp(模板+例题)

    文章参考:数位dp之总结 首先,什么是数位dp?它是干什么的? 数位dp是一种计数用的dp,一般就是要统计一个区间[le,ri]内满足一些条件数的个数. 举个栗子: 加入我们要枚举所有上界不超过231 ...

随机推荐

  1. Win10安装MySQL5和MySQL8

    1. 下载数据库,配置环境变量 因为是安装两个MySQL数据库,端口号要不一样,MySQL默认端口号是3306,建议先配置非默认端口号,以免出现问题 1.1 官网下载5.7和8.0的压缩包 我下载的是 ...

  2. 11.qml-通过方法来加载组件、字符串方式加载组件

    在上章,我们学习了10.qml-组件.Loader.Component介绍. 本章我们继续来学习组件的其它创建方式. 1.调用Function来加载和移除组件 之前我们是使用Loader对象来实现加载 ...

  3. 使用C#实现一个PPT遥控器

    说明 本项目参考了 https://github.com/yangzhongke/PhoneAsPrompter 项目来完成实现,并对其进行了一些修改完善. 完整代码可以到 https://githu ...

  4. [Windows] 屏幕截图 - FastStone Capture(FSCapture) v9.4 飞扬时空汉化绿色版(官方地址) 【清晰好用 已验证】

    [Windows] 屏幕截图 - FastStone Capture(FSCapture) v9.4 飞扬时空汉化绿色版(官方地址) [复制链接]     愤怒の葡萄 电梯直达 楼主    发表于 2 ...

  5. nano 按Ctrl+X 输入Y 回车

    如何退出nano 1.nano 按Ctrl+X 如果你修改了文件,下面会询问你是否需要保存修改. 2.输入Y确认保存,输入N不保存,按Ctrl+C取消返回.如果输入了Y,下一步会让你输入想要保存的文件 ...

  6. Zabbix 监控介绍

    Zabbix 监控介绍 1.Zabbix监控架构 2.Zabbix 优点 开源无软件成本投入 Server对设备性能要求低 支持设备多,自带多种监控模板 支持分布式集中管理,有自动发现功能,可以实现自 ...

  7. wmctrl像xmonad那样方便地用快捷键来控制任务窗口的显示

    窗口左右互搏之wmctrl篇 分类: LINUX 2012-10-24 16:34:41   一直有个念头,就是能够像xmonad那样方便地用快捷键来控制任务窗口的显示,今天弄wmctrl,刚好有时间 ...

  8. [转载]屏幕PPI、分辨率到底需要多大才能满足?

    屏幕PPI.分辨率到底需要多大才能满足? 郝蛋儿 江湖骗子 13 人赞同了该文章 最近想买一个43寸的电视,720P和1080P差了500大洋.我不禁纠结了起来.看网上争得面红耳赤,有的人说不如108 ...

  9. 若依框架前端使用antd,IE11浏览器无法正常显示问题

    话不多说,直接上才艺,找到vue.config.js,把第11行的 mock 删除掉就 IE11就正常显示了, 然而项目还是不支持IE10 以及以下版本,哪位小伙伴有解决方法,可以留言交流下

  10. PHP转JAVA开发30分钟实战攻略

    服务端开发中,有很多知识是相通的,例如mysql,redis,http协议等. 基于这些基础,在编程语言上的转变并不困难. 本文主要从下面几点出发,讲述如何快速从php开发转为java开发: 使用框架 ...