SCOI2009windy数
sol:
一道数位DP的基础题。然而还是几次没想清。
这种求区间l,r的一般都用记忆化搜索的方法实现。
如果不熟悉记搜实现的数位DP建议参考Here
\(getans(1,r)-getans(1,l-1)\)即为答案。
考虑需要记录的变量:
\(pos\)表示从高往低填到了第pos位,\(now\)表示上一位填的是\(now\),\(lim\)用于最高位限制,\(lead\)用于前导0判断。
一般而言后面两个变量都是很套路很板子的列出。
然后套上模板,注意当前位枚举的数的范围即可(受now和lim的影响)。
另外为了方便,可以把\(now\)的值赋值为-2,用于填前导0。
code:
#include<cmath>
#include<string>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define RG register
#define IL inline
#define LL long long
#define DB double
using namespace std;
IL int gi() {
RG int x=0,w=0; char ch=getchar();
while (ch<'0'||ch>'9') {if (ch=='-') w=1;ch=getchar();}
while (ch>='0'&&ch<='9') x=x*10+(ch^48),ch=getchar();
return w?-x:x;
}
int len,a[13],f[13][13];
int dfs(int pos,int now,int lim,int lead) {
if(pos>len) return 1;
if(!lim&&f[pos][now]!=-1) return f[pos][now];
RG int i,ans=0,up=lim?a[len-pos+1]:9;
for(i=0;i<=up;++i) {
if(abs(i-now)<2) continue;
if(lead&i==0) ans+=dfs(pos+1,-2,lim&&i==up,1);
else ans+=dfs(pos+1,i,lim&&i==up,0);
}
return (!lim&&!lead)?f[pos][now]=ans:ans;
}
IL int getans(int x) {
for(len=0;x;x/=10) a[++len]=x%10;
memset(f,-1,sizeof(f));
return dfs(1,-2,1,1);
}
int main()
{
RG int l=gi(),r=gi();
printf("%d\n",getans(r)-getans(l-1));
return 0;
}