初探数位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…

使用标定板对相机位姿进行估计

使用标定板几个特定的点&#xff0c;来对相机相对标定板平面进行位姿估计。 首先进行相机的畸变校正&#xff0c;之后同个各个标定板间的圆点距离进行位姿估计。 gen_caltab (7, 7, 0.002, 0.5, C:/Users/22967/Desktop/新建文件夹/111.descr, C:/Users/22967/Desktop/新建文件…

音、视频文件格式

* 说明&#xff1a;首先要分清楚 媒体文件和编码的区别&#xff1a;文件是既包括视频又包括音频、甚至还带有脚本的一个集合&#xff0c;也可以叫容器&#xff1b;文件当中的视频和音频的压缩算法才是具体的编码。 *AVI音视频交互存储&#xff0c;最常见的音频视频容器。支持的…

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、加…

流行的音频编码标准

speech codec (G.711, G.723, G.726, G.729, iLBC) 各种各样的编解码在各种领域得到广泛的应用&#xff0c;下面就把各种codec的压缩率进行一下比较&#xff0c;不正确之处望各位同行指正。 Speech codec&#xff1a; 现主要有的speech codec 有: G.711, G.723, G.726 , G…

【angularjs】使用angular搭建项目,pc端实现网页中的内容不可复制

实现目标&#xff1a;不可复制页面内容 js:          <script language"javascript"> if (typeof(document.onselectstart) ! "undefined") { // IE下禁止元素被选取 document.onselectstart function (event){if(event.targe…

DIV+CSS如何让文字垂直居中?

在说到这个问题的时候&#xff0c;也许有人会问CSS中不是有vertical-align属性来设置垂直居中的吗&#xff1f;即使是某些浏览器不支持我只需做少许的CSS Hack技术就可以啊&#xff01;所以在这里我还要啰嗦两句&#xff0c;CSS中的确是有vertical-align属性&#xff0c;但是它…

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…

一个halcon拟合直线的例子

read_image (hImage, E:/vs2012/halcon卡尺例程/白光碗光效果4.bmp) get_image_pointer1(hImage, Pointer, Type, Width, Height) *功能&#xff1a;获取一个通道的指针&#xff0c;得到HTuple Pointer, Type, CurWidth, CurHeight dev_set_draw(margin) dev_set_color (green…

NLP数据挖掘基础知识

Basis(基础)&#xff1a; SSE(Sum of Squared Error, 平方误差和)SAE(Sum of Absolute Error, 绝对误差和)SRE(Sum of Relative Error, 相对误差和)MSE(Mean Squared Error, 均方误差)RMSE(Root Mean Squared Error, 均方根误差)RRSE(Root Relative Squared Error, 相对平方根误…

SQL Fundamentals || Oracle SQL语言

对于SQL语言&#xff0c;有两个组成部分&#xff1a; DML&#xff08;data manipulation language&#xff09; 它们是SELECT、UPDATE、INSERT、DELETE&#xff0c;就象它的名字一样&#xff0c;这4条命令是用来对数据库里的数据进行操作的语言。 DDL&#xff08;data defini…

圆形卡尺测量后创建模板

read_image (Image, QQ图片20201113111404.jpg) dev_close_window () dev_open_window_fit_image (Image, 0, 0, -1, -1, WindowHandle) dev_display (Image) rgb1_to_gray (Image,Image) ****创建模板阶段 *大致找内圆 fast_threshold (Image, Region, 128, 255, 20) connecti…

fread函数和fwrite函数,read,write

fread函数和fwrite函数 1.函数功能 用来读写一个数据块。 2.一般调用形式 fread(buffer,size,count,fp); fwrite(buffer,size,count,fp); 3.说明 &#xff08;1&#xff09;buffer&#xff1a;是一个指针&#xff0c;对fread来说&#xff0c;它是读入数据的存放地址。对fwrit…

微信小程序 CSS filter(滤镜)的使用示例

前言 之前在看七月老师的视频的时候&#xff0c;看到了有一个样式是-webkit-filter&#xff0c;不知道是什么&#xff08;我没咋学过CSS&#xff0c;嘿嘿&#xff0c;所以不知道是啥&#xff09;&#xff0c;于是查了一下&#xff0c;原来是滤镜吖。但是在微信小程序里使用的时…

vmware ubuntu重置root密码

1.重启ubuntu&#xff0c;按住shift&#xff08;开机启动时&#xff09; 2.选择recovery mode,enter 3.root选择root drop to root shell prompt 4.进入shell界面设置密码 (1)mount -rw -o remount / (2)passwd username(设置root用户的密码) 完成以上修改后&#xff0c;重启就…

halcon使用直线标定板,标定相机内参代码

read_image (Image, 直线标定板图片/Left201118140641772.bmp) get_image_size (Image, Width, Height) dev_close_window () dev_open_window_fit_image (Image, 0, 0, -1, -1, WindowHandle) dev_display (Image) * Image Acquisition 01: Code generated by Image Acquisiti…