洛谷 - 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数? 输入 包含两个整数 ...
随机推荐
- C#对二进制文件的特定位置进行读写小结
虽然网络上关于“C#对二进制文件进行读写”的文章多如牛毛,但具体到自己要处理的问题时,难免让人产生“书到用时方恨少”和“纸上读来终觉浅”的感觉.我现在感觉要真正解决自己的问题,最终还是要靠自己下功夫. ...
- Solidworks工程图 如何绘制向视图,辅助视图
先画一条垂直于视野方向的中心线 点击辅助视图,然后点选刚才的中心线即可生成向视图 对向视图的标准可以得到一些用其他方法不好标注的尺寸
- js 验证 输入值 全是中文
1.代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF- ...
- vue 实现 扫二维码 功能
前段时间一直在研究,如何通过 vue 调用 相机 实现 扫一扫的功能,但是查看文档发现,需要获取 getUserMedia 的属性值,但存在兼容性问题. 退而求其次,通过 h5plus 来实现. 1. ...
- 《python源代码剖析》笔记 Python的编译结果
本文为senlie原创.转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.python的运行过程 1)对python源码进行编译.产生字节码 2)将编译结果交给p ...
- Windows下也能够使用osw追朔系统历史性能
1.Windows系统历史性能分析困难背景 在Linux/Unix上.要追朔历史性能,一般採用部署nmon进行性能监控採集与存储的方式实现.可是却没有在Windows上的版本号. Windows系统假 ...
- 写入文本文件时“\n”不是回车换行而是个方块“■”的解决方法
用“\n”写入文本文件时,打开文本文件显示的为什么不是回车换行而是个黑方块“■”,但用file()读取时还是认为是一行一行的? 首先在WINDOWS里回车换行是"\r\n"; 而L ...
- TypeError: 'module' object is not callable 原因分析
程序代码 class Person: #constructor def __init__(self,name,sex): self.Name = name self.Sex = sex def ToS ...
- 解析SQL中的包含的列和表
using System; using System.IO; using System.Collections.Generic; namespace SQLProcess { class Progra ...
- 【Nginx安装】CentOS7安装Nginx及配置
[Nginx安装]CentOS7安装Nginx及配置 2018年03月05日 11:07:21 阅读数:7073 Nginx是一款轻量级的网页服务器.反向代理服务器.相较于Apache.lighttp ...