浅说——数位DP
老子听懂了!!!!!
好感动!!!
不说多了:Keywords: 数位DP,二进制,异或。
“在信息学竞赛中,有一类与数位有关的区间统计问题。这类问题往往具有比较浓厚的数学味道,无法暴力求解,需要在数位上进行递推等操作。”——刘聪《浅谈数位类统计问题》
这类问题往往需要一些预处理,这就用到了数位DP。
需要统计区间[l,r]的满足题意的数的个数,这往往可以转换成求[0,r]-[0,l)
基本思想与方法
有了上述性质,我们就可以从高到低枚举第一次<n对应位是哪一位。
这样之前的位确定了,之后的位就不受n的限制即从00...0~99...9,可以先预处理,然后这时就可以直接统计答案。
预处理F数组。
F[i,st] 代表 位数为i(可能允许前导0。如00058也是个5位数),状态为st的方案数。这里st根据题目需要确定。
如i=4,f[i,st]也就是0000~9999的符合条件的数的个数(十进制)
决策第i位是多少(such as 0~9)
F[i,st] = F[i,st] + f[i–1,st']
st'为相对应的状态
参照刚刚所说的基本思路。预处理f数组,然后统计[0,m] - [0,n).
f[i,j]代表开头是j的i位数中不含"62"或"4"的数有几个。
如f[2,6]包含60,61,63,65,66,67,68,69
for(i=;i<=;i++)//因为数据为1000000,所以预处理7位
for(j=;j<=;j++)//第i位
for(k=;k<=;k++)//第i-1位
if(j!=&&!(j==&&k==))f[i][j]+=f[i-][k];
接下来,怎么算出0-n和0-m区间的答案数呢?
用一个通用函数(Cal):
如456=f[][]+f[][]+f[][]+f[][]+f[][]//(为什么不枚举到5呢?因为再下一位枚举了) +f[][]+f[][]+f[][]+f[][]+f[][]//(就是这一位) +f[][]+f[][]+f[][]+f[][]+f[][]+f[][]+f[][].
具体代码如下:
#include<cstdio>//最右边是第一位
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
int f[][];
int Cal(int k)//求1~k中有多少符合的数.
{
int len,digit[],i,j,ans=;
memset(digit,,sizeof(digit)),len=;//digit[i]为当前的某个数从右到左第i个位置的数是多少.
while(k>){digit[++len]=k%;k/=;}
for(i=len;i>=;i--)
{
for(j=;j<=digit[i]-;j++)//每一位只能到k的下一位,所以计算的数实际只能到k-1.所以Cal()中传数要加1.
{
if(j!=&&!(j==&&digit[i+]==))ans+=f[i][j];
}
if(digit[i]==||(digit[i]==&&digit[i+]==))break; //如果这一位本来就没法,则后面的情况报废
}
return ans;
}
int main()
{
int n,m,i,j,k;
memset(f,,sizeof(f));//f[i][j]为以j开始的且不含"62"和"4"位数为i的个数.
f[][]=;
for(i=;i<=;i++)
{
for(j=;j<=;j++)//第i位
{
for(k=;k<=;k++)//第i-1位
{
if(j!=&&!(j==&&k==))f[i][j]+=f[i-][k];
}
}
}
while()
{
scanf("%d %d",&n,&m);
if(n==&&m==)break;
printf("%d\n",Cal(m+)-Cal(n));//因为当前的Cal(k)是计算出从1到k-1的符合条件的数的个数,所以要计算n~m的个数要用Cal(m+1)-Cal(n).
}
return ;
}
变式模板题_P2657 [SCOI2009]windy数
这题还是很简单的啦(差点没做出来
个位打表大佬请离开(包括记搜),我这里讲的是DP!!!
首先Cal(b+1)-Cal(a),大家都懂吧(算了,复制一遍吧<<((因为当前的Cal(k)是计算出从1到k-1的符合条件的数的个数,所以要计算a~b的个数要用Cal(b+1)-Cal(a).))>>)
f[i][j]定义一样,以j开始的且符合条件的总位数为i的答案个数.(好绕啊)
预处理转移不用讲吧:f[i][j]+=f[i-1][k];(还是复制了)
有个小细节,每个一位数答案都为1,所以分f[1][j]=0.
重点讲讲不同之处(Cal函数):
显然位数比x要小的数字都是合法的都在[1,x)区间内,直接统计就行.(第一次加ans)
位数和x一样最高位的数字比x小的数字都是合法的都在[1,x)区间内直接统计就行(第二次加ans)
位数和x一样,最高位又和x一样我们从左到右扫一遍x各个位子上的数字大小然后枚举合法的该位子上的数[0,9]判断是否合法就行。(第三次加ans)
#include<bits/stdc++.h>
using namespace std;
int f[][];
int a,b;
int digit[],cnt,ans;
void init ()
{
for (int i=;i<=;i++) f[][i]=;
for (int i=;i<=;i++)
for (int j=;j<=;j++)
for (int k=;k<=;k++)
if(abs(j-k)>=)
f[i][j]+=f[i-][k];
}
int Cal(int x)
{
//freopen("a.in", "r", stdin);
memset(digit,,sizeof(digit));
ans=;
cnt=;
while(x)
{
digit[++cnt]=x%;
x/=;
}
//三种情况
for (int i=;i<cnt;i++)
for (int j=;j<=;j++)
ans+=f[i][j]; //在不到x位数前,所有情况符合。
for (int i=;i<digit[cnt];i++) ans+=f[cnt][i]; //x位数,最高位未到digit[cnt]。
for (int i=cnt-;i>=;i--)//x位数,最高位到digit[cnt]
{
for (int j=;j<digit[i];j++)
if(abs(j-digit[i+])>=)
ans+=f[i][j];
if(abs(digit[i]-digit[i+])<)
break;
}
//printf("%d\n",ans);
return ans;
}
void work()
{
cin>>a>>b;
cout<<Cal(b+)-Cal(a)<<'\n';
}
int main()
{
init();
work();
return ;
}
同步题解
加油……
浅说——数位DP的更多相关文章
- 【BZOJ1662】[Usaco2006 Nov]Round Numbers 圆环数 数位DP
[BZOJ1662][Usaco2006 Nov]Round Numbers 圆环数 Description 正如你所知,奶牛们没有手指以至于不能玩"石头剪刀布"来任意地决定例如谁 ...
- bzoj1026数位dp
基础的数位dp 但是ce了一发,(abs难道不是cmath里的吗?改成bits/stdc++.h就过了) #include <bits/stdc++.h> using namespace ...
- uva12063数位dp
辣鸡军训毁我青春!!! 因为在军训,导致很长时间都只能看书yy题目,而不能溜到机房鏼题 于是在猫大的帮助下我发现这道习题是数位dp 然后想起之前讲dp的时候一直在补作业所以没怎么写,然后就试了试 果然 ...
- HDU2089 不要62[数位DP]
不要62 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submis ...
- 数位DP GYM 100827 E Hill Number
题目链接 题意:判断小于n的数字中,数位从高到低成上升再下降的趋势的数字的个数 分析:简单的数位DP,保存前一位的数字,注意临界点的处理,都是套路. #include <bits/stdc++. ...
- 数位dp总结
由简单到稍微难点. 从网上搜了10到数位dp的题目,有几道还是很难想到的,前几道基本都是模板题,供入门用. 点开即可看题解. hdu3555 Bomb hdu3652 B-number hdu2089 ...
- 数位DP入门
HDU 2089 不要62 DESC: 问l, r范围内的没有4和相邻62的数有多少个. #include <stdio.h> #include <string.h> #inc ...
- 数位DP之奥义
恩是的没错数位DP的奥义就是一个简练的dfs模板 int dfs(int position, int condition, bool boundary) { ) return (condition ? ...
- 浅谈数位DP
在了解数位dp之前,先来看一个问题: 例1.求a~b中不包含49的数的个数. 0 < a.b < 2*10^9 注意到n的数据范围非常大,暴力求解是不可能的,考虑dp,如果直接记录下数字, ...
随机推荐
- Ubuntu下可以直接安装mingw(sudo apt-get install mingw32 mingw32-binutils mingw32-runtime,附例子,简单好用,亲测成功)good
Mingw:在Linux系统下编译Windows的程序 Ubuntu下可以直接安装:sudo apt-get install mingw32 mingw32-binutils mingw32-runt ...
- 好用的Markdown 编辑器及工具
Markdown 是 2004 年由 John Gruberis 设计和开发的纯文本格式的语法,所以通过同一个名字它可以使用工具来转换成 HTML.readme 文件,在线论坛编写消息和快速创建富文本 ...
- 大约Android远程监控APP源代码
这篇文章的目的,关心询问名人,要打开源代码.这里说明,远程监控摄像头场外,相反,用手机摄像头摄像头server上,要理解这一点.关于非常网上的文章达到server道路.它能够准确,念就乱发博文,当然假 ...
- opencart源码解析之 index.php
//访问index.php,安全过滤.加载配置文件.核心启动文件.函数库.类库 //转载请注明: http://blog.csdn.net/dabao1989/article/details/2122 ...
- WPF ContextMenu 在MVVM模式中绑定 Command及使用CommandParameter传参
原文:WPF ContextMenu 在MVVM模式中绑定 Command及使用CommandParameter传参 ContextMenu无论定义在.cs或.xaml文件中,都不继承父级的DataC ...
- ELINK离线编程器版本说明
ELINK离线编程器版本详情,ELinkPROG版本与固件版本须匹配使用! 编程器支持芯片详细列表参见 https://www.cnblogs.com/raswin/p/9303300.html
- Linux下的软件安装
在线安装 APT:advanced packaging Tool,Debian及其派生的发行版的软件包管理工具,包含以apt-开头的多个工具,如apt-get,apt-cache,apt-cdrom ...
- Webpack Waringing Critical dependencies
报错信息如下: 出错原因先上图: 未注释部分使用了require的同时使用了es6的语法. 官方解释如下: 解决方法就是放开代码中的注释部分,不要使用es6语法
- Markdown的选择
直击现场 我一直在思索用什么格式存储文档比较好.之前一直在用google docs,但是它的格式不公开,上传/下载的时候需要转换格式,转换的时候必然会丢失一些信息.于是后来想,那还是纯本文或者mark ...
- Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)
When compiling Qt you can choose one of these options based on the configure command line: no OpenSS ...