洛谷 - P2657 - windy数 - 数位dp
https://www.luogu.org/problemnew/show/P2657
不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。
这道题是个显然到不能再显然的数位dp了。
来个最神奇的dp[i][j]表示i位数,开头为j的windy数的个数吧。
那么dp[i][j]的求法是很显然的,写个sum数组求和更为方便。
那么怎么统计windy数呢?
约定由布丁酱写的数位dp,求闭区间[l,r]时,使用count(r)-count(l-1),也就是count(x)表示不小于的[0,x]中windy数的个数。
怎么求count呢,最简单的思路就是分类讨论。
对最高位分三类:
最高位为0,遍历所有的dp[i][1~9]再加上0本身。
最高位为非0且比x最高位小,后面满足条件的可以随意取。
最高位相等,交给下一位去做。
对非最高位分两类:
该位在受前面位的限制下任取,后面满足条件的任取。
该位相等,交给下一位去做。(小心该位相等时非法,找了很久!)
最后补上x本身的判断。
最后要小心爆int……
意思是说,在数位dp的时候,每一步分类要不重不漏,每一步要判断合法。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll dp[][]; ll sum(int i,int j1,int j2){
/*if(i==0)
return 1;*/
if(j1<)
j1=;
if(j2>)
j2=;
ll res=;
for(int j=j1;j<=j2;j++){
res+=dp[i][j];
}
return res;
} void init(){
for(int j=;j<=;j++)
dp[][j]=;
for(int i=;i<=;i++){
for(int j=;j<=;j++){
dp[i][j]=sum(i-,,j-)+sum(i-,j+,);
}
} /*for(int j=1;j<=2;j++){
dp[9][j]=sum(8,0,j-2)+sum(8,j+2,9);
}*/ /*for(int i=1;i<=9;i++){
for(int j=0;j<=9;j++){
printf("dp[%d][%d]=%d\n",i,j,dp[i][j]);
}
printf("\n");
}*/
} ll A,B;
int digit[]; ll count(ll x){
//cout<<"x="<<x<<endl;
if(x==)
return ;
//否则岂不是0位数?
ll k=,cx=x;
digit[k++]=;
//占位用的
while(cx){
digit[k++]=cx%;
cx/=;
}
k--;
digit[k+]=;
//也是占位 ll res=;
for(int i=k;i>=;i--){
//printf("res=%d\n",res);
if(i==k){
//最高位取0
for(int ii=i-;ii>=;ii--)
res+=sum(ii,,);
res+=;//0
//其实不用特判啊
for(int j=;j<digit[i];j++){
//最高位比digit小且不为0
if(i->=){
res+=sum(i-,,j-)+sum(i-,j+,);
}
else{
res+=;
}
}
//最高位就取相等,问题留给下一次循环
}
else{
//前面的位都相等,非最高位的情况,双重受限
for(int j=;j<digit[i];j++){
if(abs(digit[i+]-j)<=)
;//被前一位限制取值
else if(i->=){
res+=sum(i-,,j-)+sum(i-,j+,);
}
else{
res+=;
}
}
}
} for(int i=;i<=k;i++){
if(abs(digit[i]-digit[i-])<=){
//printf("res1=%d\n",res);
return res;
}
}
//printf("res2=%d\n",res+1);
return res+;
} int main(){
init();
while(~scanf("%lld%lld",&A,&B)){
printf("%lld\n",count(B)-count(A-));
}
}
后来学会了另一种写法。搜索写法。这种写法里要用到lead来指定new_state1的状态。
#include<bits/stdc++.h>
using namespace std;
#define ll long long int a[];
ll dp[][/*前一位的取值,只有0~9*/];//不同题目状态不同
ll dfs(int pos,int state1/*前一位的取值,只有0~9,-1表示不受限制*/,bool lead/*这一位的前面是否为零*/,bool limit/*这一位是否取值被限制(也就是上一位没有解除限制)*/)
//不是每个题都要处理前导零
{
//递归边界,最低位是0,那么pos==-1说明这个数枚举完了
if(pos==-)
return ;/*这里返回1,表示枚举的这个数是合法的,那么这里就需要在枚举时必须每一位都要满足题目条件,也就是说当前枚举到pos位,一定要保证前面已经枚举的数位是合法的。 */
//第二个就是记忆化(在此前可能不同题目还能有一些剪枝)
if(!limit && !lead && dp[pos][state1]!=-)
return dp[pos][state1];
/*常规写法都是在没有限制的条件记忆化,这里与下面记录状态对应*/
int up=limit?a[pos]:;//根据limit判断枚举的上界up
ll ans=;
//开始计数
for(int i=; i<=up; i++) { //枚举,然后把不同情况的个数加到ans就可以了
int new_state1=i;
if(lead==true&&i==){
//这一位继续是前导0,new_state1应为-1
new_state1=-;
}
if(state1>=&&abs(new_state1-state1)<)
continue;
/*
计数的时候用continue跳过不合法的状态,不再搜索
*/ //合法的状态向下搜索
ans+=dfs(pos-,new_state1,lead && i==,limit && i==a[pos]);//最后两个变量传参都是这样写的
}
//计算完,记录状态
if(!limit && !lead)
dp[pos][state1]=ans;
/*这里对应上面的记忆化,在一定条件下时记录,保证一致性,当然如果约束条件不需要考虑lead,这里就是lead就完全不用考虑了*/
return ans;
} ll solve(ll x) {
//可能需要特殊处理0或者-1
if(x<=)
return ; int pos=;
while(x) { //把数位分解
a[pos++]=x%;//编号为[0,pos),注意数位边界
x/=;
} return dfs(pos-/*从最高位开始枚举*/,-/*表示这一位前面并没有对其限制的高位*/,true,true);//刚开始最高位都是有限制并且有前导零的,显然比最高位还要高的一位视为0嘛
} int main() {
memset(dp,-,sizeof(dp));
//一定要初始化为-1 ll le,ri;
while(~scanf("%lld%lld",&le,&ri)) {
printf("%lld\n",solve(ri)-solve(le-));
}
}
洛谷 - P2657 - windy数 - 数位dp的更多相关文章
- 洛谷P2657 windy数 [SCOI2009] 数位dp
正解:数位dp 解题报告: 传送门! 这题一看就是个数位dp鸭,"不含前导零且相邻两个数字之差至少为2"这种的 然后就直接套板子鸭(板子戳总结,懒得放链接辣QAQ 然后就是套路 然 ...
- 洛谷P2657 windy数
传送 裸的数位dp 看这个题面,要求相邻两个数字之差至少为2,所以我们记录当前填的数的最后一位 同时要考虑毒瘤的前导0.如果填的数前面都是0,则这一位填0是合法的. emmm具体的看代码叭 #incl ...
- 洛谷P2657 [SCOI2009]windy数 [数位DP,记忆化搜索]
题目传送门 windy数 题目描述 windy定义了一种windy数.不含前导零且相邻两个数字之差至少为2的正整数被称为windy数. windy想知道, 在A和B之间,包括A和B,总共有多少个win ...
- luogu P2657 [SCOI2009]windy数 数位dp 记忆化搜索
题目链接 luogu P2657 [SCOI2009]windy数 题解 我有了一种所有数位dp都能用记忆话搜索水的错觉 代码 #include<cstdio> #include<a ...
- P2657 [SCOI2009]windy数 数位dp
数位dp之前完全没接触过,所以NOIP之前搞一下.数位dp就是一种dp,emm……用来求解区间[L,R]内满足某个性质的数的个数,且这个性质与数的大小无关. 在这道题中,dp[i][j]代表考虑了i位 ...
- 题解 BZOJ1026 & luogu P2657 [SCOI2009]windy数 数位DP
BZOJ & luogu 看到某大佬AC,本蒟蒻也决定学习一下玄学的数位$dp$ (以上是今年3月写的话(叫我鸽神$qwq$)) 思路:数位$DP$ 提交:2次 题解:(见代码) #inclu ...
- 【BZOJ-1026】windy数 数位DP
1026: [SCOI2009]windy数 Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 5230 Solved: 2353[Submit][Sta ...
- bzoj 1026 [SCOI2009]windy数 数位dp
1026: [SCOI2009]windy数 Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline ...
- 【bzoj1026】[SCOI2009]windy数 数位dp
题目描述 windy定义了一种windy数.不含前导零且相邻两个数字之差至少为2的正整数被称为windy数. windy想知道,在A和B之间,包括A和B,总共有多少个windy数? 输入 包含两个整数 ...
随机推荐
- Android Studio 2.0 Beta 5公布,修复几个与即时执行相关的严重BUG.
Android Studio 2.0 Beta 5公布,修复几个与即时执行相关的严重BUG. This build fixes a couple of important bugs related t ...
- 步步为营(十六)搜索(二)BFS 广度优先搜索
上一篇讲了DFS,那么与之相应的就是BFS.也就是 宽度优先遍历,又称广度优先搜索算法. 首先,让我们回顾一下什么是"深度": 更学术点的说法,能够看做"单位距离下,离起 ...
- linux cat 文件操作
简略版: cat主要有三大功能:1.一次显示整个文件.$ cat filename2.从键盘创建一个文件.$ cat > filename 只能创建新文件,不能编辑已有文件.3.将几个文 ...
- 通过Java反射做实体查询
我们在使用hibernate的时候,查询的时候都会和实体中的一些字段相结合去查询,当然字段少了,还算是比较简单,当字段多了,就不那么容易了,所以就自己写了个方法,根据实体中的字段信息去查询,废话不多说 ...
- SpringBoot学习之@SpringBootApplication注解
下面是我们经常见到SpringBoot启动类代码: @SpringBootApplicationpublic class DemoApplication extends SpringBootServl ...
- 横跨十年CPU架构回顾
http://cpu.zol.com.cn/209/2092791_all.html#p2092791 本文导航 第1页:K7架构 打开AMD崛起大门的钥匙 第2页:玩破解 K7时代便已经拥有 第3页 ...
- 重构机房收费系统你要用的——异常处理和抛出异常(try catch finally)——(vb.net)
你能保证你的程序不会出问题吗? 不能 当你的程序执行到某个地方发生了你不想要的结果.你是否想让它一错再错? 不想 你是否想让你的程序占着茅坑不拉屎? 不想 你是否想知道你的程序出错的原因? 想 个问题 ...
- Redis相关知识
Redis 存储的五种 字符串类型:string 一个String类型的value最大可以存储512M String是最常用的一种数据类型,普通的key/value存储. 散列类型: hash 键值 ...
- 64位CentOs7源码安装mysql-5.6.35过程分享
首先安装依赖包,避免在安装过程中出现问题 [root@bogon liuzhen]# yum -y install gcc gcc-c++[root@bogon liuzhen]# yum -y in ...
- java 调用ant的自己定义task,默认不是build.xml 的一点问题
java 调用ant的自己定义task, File buildFile = new File(".//ee-build.xml"); // 创建一个ANT项目 ...