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;
}
posted @ 2021-07-14 19:31  BFNewdawn  阅读(36)  评论(0编辑  收藏  举报