初探数位dp

  前言:这是蒟蒻第一次写算法系列,请诸位大佬原谅文笔与排版。

 


 

一、导入

  在刷题的时候,我们有时会见到这样一类问题:在区间$[l,r]$内,共有多少个整数满足某种条件。如果$l$和$r$间的差很小,我们可以考虑暴力枚举直接判断。然而,若$l<=r<=10^9$甚至更大呢?

  这时往往就可以用到一种dp方式:数位dp。


 

二、做法:

  这里先放一道例题:1026: [SCOI2009]windy数。

  题意:求在区间$[l,r]$内,满足相邻数位的差>=2的整数的个数。

  首先我们可以发现,$[l,r]$的答案=$[1,r]$的答案-$[1,l)$的答案。于是我们可以把问题转化为求$[1,r]$和$[1,l-1]$的答案。因为$l<=r<=2*10^9$,所以暴力枚举肯定行不通。但是我们可以发现这道题中整数需满足的条件只与相邻数位有关,这启示我们,也许可以按位dp?

  我们先来看一张经典的图(表示区间$[0,22]$):

  

 

  这幅图中把正整数按位用树的形式表示,那么区间$[0,x]$(这里x=22)就可以拆成多棵满10叉树(即图中的蓝圈),而且因为每层所用的树个数不会超过10棵(0~9),总共有$\log_{10}{x}$层,则树的个数规模为$O(10\log{x})$。

  那么单棵满10叉树的答案怎么求呢?我们仔细观察这棵树,那么就可以发现每棵满10叉树表示的数是位数相同(等于它从下往上数所处的层数),最高位相同(等于根节点表示的数),且该树的答案只与以树根的10个儿子为根的,10棵子树的答案有关。并且在整棵树中,处在同一层的,且子树根节点表示的数相同的树(即位数相同,最高位相同),它们是等价的。于是我们就可以直接设$f[i][j]$表示有i位,最高位为j的满足条件的整数的个数,然后xjb转移。于是就可以优哉游哉地dp, 然后按图统计答案了。

  不过这道题还是比较麻烦,因为需要排除前导零的影响,不过核心思想还是上面的那样,然后再分位数统计就好了。

  代码(时间复杂度$O(10^2\log{r})$):

#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
#define ll long long
#define ull unsigned long long
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
#define lowbit(x) (x& -x)
#define mod 1000000007
#define inf 0x3f3f3f3f
#define eps 1e-18
#define maxn 100020
inline ll read(){ll tmp=0; char c=getchar(),f=1; for(;c<'0'||'9'<c;c=getchar())if(c=='-')f=-1; for(;'0'<=c&&c<='9';c=getchar())tmp=(tmp<<3)+(tmp<<1)+c-'0'; return tmp*f;}
inline ll power(ll a,ll b){ll ans=0; for(;b;b>>=1){if(b&1)ans=ans*a%mod; a=a*a%mod;} return ans;}
inline ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
inline void swap(int &a,int &b){int tmp=a; a=b; b=tmp;}
using namespace std;
int f[20][10];
int l,r;
int dp(int x)
{int num[20],len=0;for(;x;x/=10)num[++len]=x%10;for(int i=0;i<=9;i++)f[1][i]=1;//预处理 for(int i=2;i<=len;i++)for(int j=0;j<=9;j++){f[i][j]=0;for(int k=0;k<=9;k++)if(abs(j-k)>=2)f[i][j]+=f[i-1][k];}//处理f数组,f[i][j]表示有i位,最高位为j的的windy数个数*/ int ans=0;for(int i=1;i<len;i++)for(int j=1;j<=9;j++)ans+=f[i][j];//统计位数小于len的数一定小于n,直接加上 for(int i=len;i;i--){int l=(i==len)?1:0,r=(i==1)?num[i]:num[i]-1;//不含前导零,所以最高位不能取0,从1开始枚举,否则从0开始//除个位以外,因当前位若取num[i]可能超出1~n的范围,所以只能取到num[i]-1;因为询问1~n时包含n,所以个位的上限要取num[i]for(int j=l;j<=r;j++)if(i==len||abs(j-num[i+1])>=2)ans+=f[i][j];if(i<len&&abs(num[i]-num[i+1])<2)break;//统计下一位时,这一位取的是num[i],若会和上一位num[i+1]发生冲突,则不可能出现windy数,直接break掉 
    }return ans;
}
int main()
{l=read(); r=read();printf("%d\n",dp(r)-dp(l-1));
}
bzoj1026

 


 

三、归纳:

  数位dp的特征:数据规模大,统计整数个数,被统计的数满足的条件往往与数位之间的关系或数位间的运算有关。

  基本做法:差分,先按位dp出所需数据($f[i][S]$->i位数,状态为S),然后再拆分原区间,用dp出的数据统计。

 


 

四、其他例题:

1、【bzoj1833】[ZJOI2010] count 数字计数

  裸的数位dp,分别计算每个数字出现的次数,做法和上面类似。

  代码:

#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<iostream> 
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
#define ll long long
#define ull unsigned long long
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
#define lowbit(x) (x& -x)
#define mod 1000000007
#define inf 0x3f3f3f3f
#define eps 1e-18
#define maxn 100010
inline ll read(){ll tmp=0; char c=getchar(),f=1; for(;c<'0'||'9'<c;c=getchar())if(c=='-')f=-1; for(;'0'<=c&&c<='9';c=getchar())tmp=(tmp<<3)+(tmp<<1)+c-'0'; return tmp*f;}
inline ll power(ll a,ll b){ll ans=1; for(;b;b>>=1){if(b&1)ans=ans*a%mod; a=a*a%mod;} return ans;}
inline ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
inline void swap(int &a,int &b){int tmp=a; a=b; b=tmp;}
using namespace std;
ll f[20][10][10],base[20];
ll l,r;
void prework()
{base[0]=1;for(int i=1;i<=13;i++)base[i]=base[i-1]*10;for(int i=0;i<=9;i++)f[1][i][i]=1;for(int i=2;i<=13;i++)for(int j=0;j<=9;j++){ll x=f[i-1][0][0]+f[i-1][0][1]*9;for(int k=0;k<=9;k++){f[i][j][k]=(j==k?base[i-1]:0)+x;}}
}
ll solve(ll n,int num)
{if(n<0)return 0;ll tmp=++n;//这里++n是为了把闭区间转化为开区间,因为下面求解时1~n的答案并不包括n。。int a[20],len=0;for(;tmp;tmp/=10)a[++len]=tmp%10;for(int i=1;i<len;i++)for(int j=1;j<=9;j++)ans+=f[i][j][num];for(int i=len;i;i--){for(int j=(i==len?1:0);j<a[i];j++)ans+=f[i][j][num];n-=a[i]*base[i-1];if(a[i]==num)ans+=n;}return ans;
}
int main()
{prework();l=read(); r=read();for(int i=0;i<9;i++)printf("%lld ",solve(r,i)-solve(l-1,i));printf("%lld\n",solve(r,9)-solve(l-1,9));
}
bzoj1833

 

转载于:https://www.cnblogs.com/quzhizhou/p/9245648.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/254190.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java演示手机发送短信验证码功能实现

我们这里采用阿里大于的短信API 第一步&#xff1a;登陆阿里大于&#xff0c;下载阿里大于的SDK a、在阿里大于上创建自己的应用 b、点击配置管理中的验证码&#xff0c;先添加签名&#xff0c;再配置短信模板 第二步&#xff1a;解压相关SDK&#xff0c;第一个为jar包&#xf…

ELK日志分析系统(转)

原创作品&#xff0c;允许转载&#xff0c;转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://467754239.blog.51cto.com/4878013/1700828大纲&#xff1a; 一、简介 二、Logstash 三、Redis 四、Elasticsearch 五、Kinaba 一、简介 …

Glide使用总结

首先&#xff0c;添加依赖 implementation com.github.bumptech.glide:glide:4.5.0 annotationProcessor com.github.bumptech.glide:compiler:4.5.0之后添加访问网络权限 <uses-permission android:name"android.permission.INTERNET" />一、常用的方法 1、加…

Segments POJ 3304 直线与线段是否相交

题目大意&#xff1a;给出n条线段&#xff0c;问是否存在一条直线&#xff0c;使得n条线段在直线上的投影有至少一个公共点。 题目思路:如果假设成立&#xff0c;那么作该直线的垂线l&#xff0c;该垂线l与所有线段相交&#xff0c;且交点可为线段中的某两个交点 证明&#xff…

Linux Socket编程(不限Linux)

“一切皆Socket&#xff01;” 话虽些许夸张&#xff0c;但是事实也是&#xff0c;现在的网络编程几乎都是用的socket。 ——有感于实际编程和开源项目研究。 我们深谙信息交流的价值&#xff0c;那网络中进程之间如何通信&#xff0c;如我们每天打开浏览器浏览网页时&#xff…

shell之计算文本中单词出现频率

2019独角兽企业重金招聘Python工程师标准>>> Word Frequency&#xff08;https://leetcode.com/problems/word-frequency/description/&#xff09; Example: Assume that words.txt has the following content: the day is sunny the the the sunny is is Your scr…

dyld: Library not loaded: @rpath/libswiftCore.dylib 解决方法

解决&#xff1a; 设置Build Setting - > 搜索 embe关键字 -> 修改属性 见如下图&#xff1a; 如果更新了Xcode 8 这里变成&#xff1a; 转载于:https://www.cnblogs.com/yajunLi/p/5979621.html

Dubbo之RPC架构

为什么会有dubbo的出现: 随着互联网的发展&#xff0c;网站应用的规模不断扩大&#xff0c;常规的垂直应用架构已无法应对&#xff0c;分布式服务架构以及流动计算架构势在必行&#xff0c;亟需一个治理系统确保架构有条不紊的演进。 单一应用架构 当网站流量很小时&#xff0c…

区域路由的注册机制

AreaRegistration.RegisterAllAreas() 我们新建一个名称为Admin的Area&#xff0c;VS生成下面的代码。 { action , id 我们先来看AreaRegistration这个抽象类&#xff0c;实际上&#xff0c;它只有一个核心功能&#xff0c;就是RegisterAllAreas&#xff0c;获取所有继承它的…

嵌入式Linux系统基础知识

一、嵌入式Linux系统的构成 1、硬件 2、内核 3、应用程序&#xff08;形成根文件系统&#xff09; 二、构建嵌入式Linux系统的主要任务 1、内核部分 2、应用程序部分 嵌入式Linux的开发大致可分为三个层次&#xff1a;引导装载内核、构造文件系统和图形用户界面。作为操作系统…

各种排序笔记---基于比较排序部分

1. 选择排序 selection sort 大循环 从左到右每次以一个点开始扫描array 小循环 找到从当前起始点开始的最小值 时间复杂度为O(N^2) //selection sort an array array[] public class Solution {public int[] solve(int[] array) {if (array null || array.length 0) {return…

halcon使用点拟合圆形时候,点集顺序紊乱,不影响圆形拟合效果

read_image (Image, 截图20201226094342972.bmp) * Matching 01: BEGIN of generated code for model initialization set_system (border_shape_models, false) * Matching 01: Obtain the model image * Matching 01: The image is assumed to be made available in the * Ma…

Socket理解。

其他大部分系统&#xff0c;例如CRM/CMS/权限框架/MIS之类的&#xff0c;无论怎么复杂&#xff0c;基本上都能够本地代码本地调试&#xff0c;性能也不太重要。&#xff08;也许这个就是.net的企业级开发的战略吧&#xff09; 可是来到通讯系统&#xff0c;一切变得困难复杂。原…

多元化时代敏捷软件开发的崛起与传统软件工程的延续

多元化时代敏捷软件开发的崛起与传统软件工程的延续 1.传统软件开发模式 1.1瀑布模型 1.1.1概念 瀑布模型&#xff0c;顾名思义&#xff0c;软件开发的过程如同瀑布飞流一般&#xff0c;自上而下&#xff0c;逐级下落。瀑布模型的核心思想是将问题按照工序进行简化&#xff0c;…

【VMware vSAN 6.6】6.2.启用性能服务:vSAN硬件服务器解决方案

目录 1. 简介 1.1.适用于HCI的企业级存储2. 体系结构 2.1.带有本地存储的服务器2.2.存储控制器虚拟系统套装的缺点2.3.vSAN在vSphere Hypervisor中自带2.4.集群类型2.5.硬件部署选项3. 启用vSAN 3.1.启用vSAN3.2.轻松安装3.3.主动测试4. 可用性 4.1.对象和组件安置4.2.重新构建…

Android eclipse导入项目后出现Unable to resolve target #39;android-17#39;解决方法

eclipse导入项目后出现Unable to resolve target android-17解决方法。在最后附带还有一种编译逻辑不成功情况解决方法。 一、问题情况 二、解决的方法 1、改动项目的目标版本号与当前Android sdk相相应的版本号 2、自己主动修复一下项目 三、这个问题不是上面的。是另外情况&a…

多个圆点,鼠标选取两个,求两个点的距离,用于计算像素尺寸(halcon实现)

read_image (Image, C:/Users/22967/Desktop/晶圆找位置/0.bmp) dev_close_window () dev_open_window_fit_image (Image, 0, 0, -1, -1, WindowHandle) dev_display (Image)binary_threshold (Image, Region1, max_separability, dark, UsedThreshold) connection (Region1, C…

修改UBOOT和LINUX调试串口(TI达芬奇芯片--DM6467)

Posted on 2011-10-31 10:53 jamiedu 阅读(889) 评论(0) 编辑 收藏 1.1 概述 TI针对DM6467提供的UBOOT和内核默认都是串口0作为调试串口输出的&#xff0c;但现在我需要使用DM6467的UART0的modem功能&#xff0c;所以修改代码&#xff0c;改变调试串口为串口2。 需要修改的主要…

halcon车刀崩边检测

list_files (新建文件夹, files, Files) read_image (Image, Files[0]) dev_close_window () get_image_size (Image, Width, Height) dev_open_window (0, 0, Width/1.5, Height/1.5, black, WindowHandle) dev_set_draw (margin) dev_set_colored (12) for Index:0 to |Files…