【题解】P2657 [SCOI2009]windy数
\(Description:\)
给出a,b,求\([a,b]\)区间满足相邻两位之间差大于等于2的数的个数
SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。
\(Sample\) \(Input:\)
1 10
\(Sample\) \(Output\)
9
\(Sample\) \(Intput:\)
25 50
\(Sample\) \(Output:\)
20
\(Solution:\)
我天,刚刚入门数位dp就刚题?
喵的,听课的时候持续掉线。。。
我的服务器被屏蔽了吧。。。
回家刻苦学习,经过又是无数次的掉线和重连。。。
好的那么来切一下这道题
发现这题可以数位dp,不会dp,写记忆化
一般数位dp的dfs都传入三个参:
当前在填数字的(len),上一个会对dfs有影响的数(\(pre || last\)),上一位有没有取到要求的边界。
形如:
inline int dfs(int len,int pre,bool limit)
那么为什么要积有没有取到边界呢?
我们考虑比如对于:5123这个范围。
如果最高位取 \(1\) 那么后面第二位可以取 \(1\) ~ \(9\) 任何与上一个差的绝对值大于2的
其他小于等于 \(4\) 的都可以这么取。
但是如果到了 \(5\) 那就不一样了,后面第二位只能取到 \(1\) ,因为不能让范围大于给出的数。
还要考虑前导 \(0\) 的问题,我们可以把 \(0\) 放在首位,但不能对后一个产生那个绝对值问题的影响,那么就要把这个影响消掉,比如如果这位是前导 \(0\) ,那么就把他弄成 \(-10\) ,这样就不会有影响了。
前导 \(0\) ,的确较难处理,较难理解,那就再来个栗子:
比如要求\(a\) ~ \(b\)的数各个位数相同的数有几个,那么 \(111,222,333\) 什么的都算,但如果有一个前导 \(0\)
就凉了。。。他会把 \(0111\) 判作非法。
上代码把:
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef long long LL;
int l,r,cnt,ans;
const int N=15;
int f[N][N],digit[N];
inline void solve(int n){
cnt=0;
while(n){
digit[++cnt]=n%10;
n/=10;
}
for(int i=1;i+i<=cnt;++i) swap(digit[i],digit[cnt-i+1]);
}
inline int dfs(int len,int last,bool limit){
if(len==cnt) return 1;
if(last>=0 && !limit && f[len][last]!=-1) return f[len][last];
int ret=0,up_bound=(limit)?digit[len+1]:9;
for(int i=0;i<=up_bound;++i){
if(abs(i-last)<2) continue;
int pre=i;
if(last==-10 && i==0) pre=last;
ret+=dfs(len+1,pre,limit&&i==digit[len+1]);
}
if(last>=0 && ret) f[len][last]=ret;
return ret;
}
signed main(){
scanf("%lld%lld",&l,&r);
memset(f,-1,sizeof(f));
solve(r);
ans=dfs(0,-10,true);
solve(l-1);
memset(f,-1,sizeof(f));
printf("%lld\n",ans-dfs(0,-10,true));
return 0;
}
