P2657 [SCOI2009] windy 数 题解
0x0F 前言
撒币数位 \(DP\) 坑杀我也
没有模板自己敲了快两个小时的我是屑
(还不得不向度娘求教)
0x01 读题
\(……\)
0x02 基础分析
找符合规定的数,显然是数位\(DP\)。
(以下是没有板子的讲解)
\(0\sim9\)都是\(windy\)数,所以先预处理一下
for(int i=0;i<=9;i++)
f[1][i]=1;
既然要做到每一位的递推,自然要把 \(a\) 和 \(b\) 拆开
while(x){
cf[++len]=x%10;
x/=10;
}
多举例子多算,易得状态转移方程。
0x03 状态转移方程
对于以 \(i\) 为开头,长度为 \(j\) 的数,有
\(f[i][j]+=f[i-1][k];\)
0x04 上界
数位\(DP\) 难就难在它的上界处理
我们不妨分情况讨论
1.数位小于 \(b\)
一加就好 \(:)\)
for(int i=1;i<=len-1;i++){
for(int j=1;j<=9;j++)
ans+=f[i][j];
}
2.数位等于 \(b\) 但最高位小于 \(b\) 的最高位
一加就好 \(:)\)
for(int i=1;i<cf[len];i++)
ans+=f[len][i];
3.数位等于 \(b\) 且最高位等于 \(b\) 的最高位
不太好搞 \(:(\)
需要从最高位后面开始枚举
for(int i=len-1;i>=1;i--){
for(int j=0;j<=cf[i]-1;j++){
if(abs(j-cf[i+1])>=2)
ans+=f[i][j];
}
于是乎,基本上做完了 \(qwq\)
0x05 代码
#include<bits/stdc++.h>
using namespace std;
const int N=21000;
int a,b;
int f[N][N];
int work(int x){
int cf[N];
int len=0,ans=0;
while(x){
cf[++len]=x%10;
x/=10;
}
for(int i=1;i<=len-1;i++){
for(int j=1;j<=9;j++)
ans+=f[i][j];
}
for(int i=1;i<cf[len];i++)
ans+=f[len][i];
for(int i=len-1;i>=1;i--){
for(int j=0;j<=cf[i]-1;j++){
if(abs(j-cf[i+1])>=2)
ans+=f[i][j];
}
if(abs(cf[i+1]-cf[i])<2)
break;
}
return ans;
}
int main(){
scanf("%d%d",&a,&b);
for(int i=0;i<=9;i++)
f[1][i]=1;
for(int i=2;i<=10;i++)
for(int j=0;j<=9;j++)
for(int k=0;k<=9;k++)
if(abs(j-k)>=2)
f[i][j]+=f[i-1][k];
cout<<work(b+1)-work(a);
return 0;
}