数位dp 模板加例题
概念:所谓数位”dp“,是指对数字的”位“进行的与计数有关的DP。一个数一个位,十位,百位,千位等,数的每一位就是数位。数位DP用来解决与数字操作有关的问题。例如数位之和的问题。特定数字问题等。这些问题的特征就是给定的区间不能超级大,不用用暴力的方法逐个检查,必须接近O(log2n) 复杂度的算法。解题的思路是用DP对”数位“进行操作,记录已经算过的区间状态,用在后续计算中,快速进行大范围的筛选。
实现方法:1.递推实现 2.用记忆法搜索实现。
一.递推的实现:
我们来看一到例题:统计[0,n]内不含4的数字个数(直接上代码)
#include<cstdio>
#include<algorithm>
#include<set>
#include<iostream>
using namespace std;
const int maxn=2e5+5;
const int M=15;
int dp[M][M]; //dp[i][j]表示i位数字第一个数是j是符合条件的数字数量;
int bit[M]; //bit[i]存第i位数字;
void init()
{
dp[0][0]=1;
for(int i=1;i<=M;i++)
{
for(int j=0;j<10;j++)
{
for(int k=0;k<10;k++)
{
if(j!=4) dp[i][j]+=dp[i-1][k];
}
}
}
}
//计算[0,n]区间内满足条件数字的个数;
int solve(int len)
{
int ans=0;
for(int i=len;i>=1;i--) //从高位到地位处理;
{
for(int j=0;j<bit[i];j++)
if(j!=4) ans+=dp[i][j];
if(bit[i]==4) {ans--;break;} //i位开头是4的都不行;
}
return ans;
}
int ok(int x) //简单的一个求bit数组以及n的位数;
{
int len=0;
while(x)
{
bit[++len]=x%10;
x/=10;
}
return solve(len);
}
int main()
{
int n;
init(); //预计算dp[][];
cin>>n;
printf("%d\n",ok(n)+1); //不要忘记0也是符合条件;
return 0;
}
二.记忆化搜索:
记忆化搜索的思路其实是在递归程序dfs中搜索所以可能的情况,但是遇到已经算过的记录在dp[ ]中的结果就可以直接使用,减少了重复计算。然后记忆化搜索是有模板(这让我这种dp菜鸡有一丝丝动力)
const int M=15;
int dp[M][sta]; //sta对应不同转态;
int bit[M];
int dfs(int pos,/*sta变量*/,int lead/*前导零*/,int lim/*数位上界变量*/)
{
if(pos==-1) return 1; //判断是否枚举到最低位;或者有些题目用于剪枝;
if(!lim&&dp[pos][sta]!=-1) return dp[pos][sta];
int up=lim?bit[pos]:9;
int ans=0; //计数;
for(int i=0;i<=up;i++)
{
if()...
else if()...
//不同题目的要求不同;
//这里是记忆化搜索最灵活的部分要具体题目分析;
ans+=dfs(pos-1,/*sta状态转移*/,lead&&i==0,lim&&i==bit[pos]);
}
if(!lim) dp[pos][sta]=ans;
return ans;
}
int solve(int x)
{
int len=0;
while(x)
{
bit[len++]=x%10;
x/=10;
}
return dfs(len-1,/*从最高位开始枚举sta状态*/,/*一系列状态*/,1);
}
int main()
{
int l,r;
while(~scanf("%d%d",&n,&m))
{
memset(dp,-1,sizeof dp); //dp数组初始化,这个也可以放在while外面;
printf("%d\n",solve(r)-solve(l-1));
}
return 0;
}
看了模板我们来看几题例题:
1.Hdu 2089 题意是统计[n,m]内不含4且没有“62”的数:
代码如下:
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<iostream>
#include<string>
using namespace std;
typedef unsigned long long ull;
const int maxn=4e2+5;
const int Inf=0x3f3f3f3f;
int dp[15][15];
int bit[15];
int dfs(int pos,int pre,int sta,int lim) //pre记录前一个数字;
{
if(pos<=-1) return 1;
if(!lim&&dp[pos][sta]!=-1) return dp[pos][sta];
int up=lim?bit[pos]:9;
int ans=0;
for(int i=0;i<=up;i++)
{
if(pre==6&&i==2) continue; //如果前一个数字是六现在补上的是2那么跳过循环;
if(i==4) continue;
ans+=dfs(pos-1,i,i==6,lim&&i==bit[pos]);
}
if(!lim) dp[pos][sta]=ans;
return ans;
}
int solve(int x)
{
int pos=0;
while(x)
{
bit[pos++]=x%10;
x/=10;
}
return dfs(pos-1,0,0,1);
}
int main()
{
int l,r;
memset(dp,-1,sizeof dp);
while(~scanf("%d%d",&l,&r)&&r+l)
{
printf("%d\n",solve(r)-solve(l-1));
}
return 0;
}
2.Hdu 3652 题意是求[1,n]中能被13整除且包含“13”的数。这题较上题多一个整除的判断处理需要多开一维来记录余数:
代码如下:
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<iostream>
#include<string>
using namespace std;
typedef unsigned long long ull;
const int maxn=1e5+5;
int dp[15][15][3];
//dp[i][j][k]
//i:数位
//j:余数
//k:3种操作状况,0:末尾不是1 , 1:末尾是 1 ,2:含有13
int bit[15];
int dfs(int pos,int mod,int have,int lim)
{
if(pos<=0) return mod==0&&have==2; //枚举完成返回合理的数字即 能被13整除且包含“13”;
if(!lim&&dp[pos][mod][have]!=-1) return dp[pos][mod][have];
int up=lim?bit[pos]:9;
int mod_x,have_x;
int ans=0;
for(int i=0;i<=up;i++)
{
mod_x=(mod*10+i)%13; //其实是模拟除法;
have_x=have;
if(have==0&&i==1) //末尾数字是不是1现加入1,末尾为1;
have_x=1;
if(have==1&&i!=1) //末尾是1 现加入的不为1 末尾不是1;
have_x=0;
if(have==1&&i==3) //包含“13” 标记为2;
have_x=2;
ans+=dfs(pos-1,mod_x,have_x,lim&&i==bit[pos]);
}
if(!lim) dp[pos][mod][have]=ans;
return ans;
}
int solve(int x)
{
int len=0;
while(x)
{
bit[++len]=x%10;
x/=10;
}
return dfs(len,0,0,1);
}
int main()
{
int n;
memset(dp,-1,sizeof dp);
while(~scanf("%d",&n)) cout<<solve(n)<<endl;
return 0;
}
这两题是比较基础的数位dp,主要用于熟悉记忆化搜索的模板(hdu 3555类似于HDU2089 大家可以试试)
比较难的有 hdu 6148,hdu 4507大家可以挑战挑战。
个人感觉数位dp比一般的dp更容易理解,大家多花点时间效果是很明显的。
数位dp 模板加例题的更多相关文章
- HDU 2089 不要62(数位dp模板题)
http://acm.hdu.edu.cn/showproblem.php?pid=2089 题意:求区间内不包含4和连续62的数的个数. 思路: 简单的数位dp模板题.给大家推荐一个好的讲解博客.h ...
- POJ 3286 How many 0's(数位DP模板)
题目链接:http://poj.org/problem?id=3286 题目大意: 输入n,m,求[n,m]的所有数字中,0出现的总数是多少,前导零不算. 解题思路: 模板题,设dp[pos][num ...
- 数位dp模板 [dp][数位dp]
现在才想到要学数位dp,我是不是很弱 答案是肯定的 以一道自己瞎掰的题为模板 //题: //输入数字n //从0枚举到n,计算这n+1个数中含有两位数a的数的个数 //如12930含有两位数93 #i ...
- 51nod 1009 数字1的数量(数位dp模板)
给定一个十进制正整数N,写下从1开始,到N的所有正数,计算出其中出现所有1的个数. 例如:n = 12,包含了5个1.1,10,12共包含3个1,11包含2个1,总共5个1. 数位dp的模板题 ...
- 51nod 1009 - 数字1的数量 - [数位DP][模板的应用以及解释]
题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1009 基准时间限制:1 秒 空间限制:131072 KB 给 ...
- HDU - 4722 Good Numbers 【找规律 or 数位dp模板】
If we sum up every digit of a number and the result can be exactly divided by 10, we say this number ...
- 【hdu6148】Valley Numer【数位dp模板题】
题意 对于每组数据给出一个整数n(length(n)<=100),找出不大于n的数字中有多少是Valley Numer.对于Valley的定义是它每一位的数字要么是递增,要么是递减,要么是先递减 ...
- HDU 3555 Bomb(数位DP模板啊两种形式)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3555 Problem Description The counter-terrorists found ...
- HDU 2089 不要62 数位DP模板题
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2089 参考博客:https://www.cnblogs.com/HDUjackyan/p/914215 ...
随机推荐
- Vue 面试题汇总
Vue 面试题汇总 refs xgqfrms 2012-2020 www.cnblogs.com 发布文章使用:只允许注册用户才可以访问!
- es6 curry function
es6 curry function // vuex getters export const getAdsFilterConfig = (state) => (spreader) => ...
- TypeScript callback Object params
TypeScript callback Object params 回调函数 对象 参数 const func = (options = {}) => { // do somthing retu ...
- js 深入原理讲解系列-currying function
js 深入原理讲解系列-currying function 能看懂这一题你就掌握了 js 科里函数的核心原理 不要专业的术语,说人话,讲明白! Q: 实现 sum 函数使得以下表达式的值正确 cons ...
- angular-2-tutorial-2017
# angular-2-tutorial-2017https://www.sitepoint.com/understanding-component-architecture-angular/http ...
- yarn create & npx & npm init
yarn create & npx & npm init https://www.npmtrends.com/npm-vs-npx-vs-yarn demo https://www.n ...
- CSS3 & gradient & color & background
CSS3 & gradient & color & background css background https://developer.mozilla.org/en-US/ ...
- WPF 关于ComboBox在前台绑定XML数据的一些方法,使用XML数据提供器 XmlDataProvider
关于使用 数据提供器:XmlDataProvider 的一些问题,以及在WPF中是如何使用的一些介绍,还有踩到的一些坑,希望其他和我碰到一样问题的,可以更快的解决. 首先,要求是 在WPF 的前台代码 ...
- SpringBoot2.x整合Email并利用AOP做一个项目异常通知功能
因为不知aop能干嘛,因此用aop做个小功能,再结合最近学的springboot-Email做了个系统异常自动邮件通知的功能, 感觉满满的成就感. AOP不懂的可以看上一篇:https://www.c ...
- Linux fork()一个进程内核态的变化
[前言]用户态的变化,耳熟能详不在赘述.现在支持读时共享,写时复制. 一.内核态的变化 1.fork一个子进程代码 #include <stdio.h> #include <stdl ...