洛谷 - 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数? 输入 包含两个整数 ...
随机推荐
- 79.iOS 设备的UI规范和iOS各控件默认高度
iOS设备的UI 规范 iPhone界面尺寸 iPhone图标尺寸 iPad的设计尺寸 iPad图标尺寸 iPhone设备尺寸分辨率比例 iPhone各设备 launch image iOS 各种控件 ...
- SolidEdge 如何绘制剖视图
如果要创建剖视图,则点击切割平面按钮,然后绘制剖面线,画好之后点击完成 然后点击剖视图按钮,鼠标单击刚才的剖面线,往要的方向拖动,即可生成剖面视图 剖视图有时也需要用到旋转剖视图 如下图所示, ...
- vue2.0 自定义过滤器(filter)实例
一.过滤器简介 (1)过滤器创建 过滤器的本质 是一个有参数 有返回值的方法 new Vue({ filters:{ myCurrency:function(myInput){ return 处理后的 ...
- android-----JNI中的log打印
1. 导入log头文件 在你使用的 .c/ .cpp 文件中 导入 log.h 头文件 #include<android/log.h> 2.在Android.mk 中 加上 LOCAL_L ...
- c程序设计语言第一章3
字符数组是C语言中最常用的数组类型.下面我们通过编写一个程序,来说明字符数组以反操作字符数组的函数的用法.该程序读入一组文本行,并把最长的文水行打印出来.该算法的基本框架非常简单: while (还有 ...
- 解决查询access数据库含日文出现“内存溢出”问题
ACCESS有个BUG,那就是在使用 like 搜索时如果遇到日文就会出现“内存溢出”的问题,提示“80040e14/内存溢出”. 会出问题的SQL: where title like '%" ...
- [转载]CodeIgniter配置之URL
应该有很多项目中会有这样的情况,通过 http://pc.local 可以访问,若通过 http://localhost/pc/public 则会出现一些图片.样式显示不到,超链接出错的情况,项目的移 ...
- Comparable 和 Comparator的理解
对Comparable 的解释 Comparable是一个排序接口 此接口给实现类提供了一个排序的方法,此接口有且只有一个方法 public int compareTo(T o); compareTo ...
- WebSocket 和 Socket 的区别
WebSocket 和 Socket 的区别 英文:TheAlchemist 链接:http://www.jianshu.com/p/59b5594ffbb0 <刨根问底 HTTP 和 We ...
- Creating a .bash_profile on your mac
A typical install of OS X won't create a .bash_profile for you. When you want to run functions from ...