Windy 数 题解

题目描述

不含前导零且相邻两个数字之差至少为 \(2\) 的正整数被称为 windy 数。windy 想知道,在 \(a\)\(b\) 之间,包括 \(a\)\(b\),总共有多少个 windy 数?

分析

数位 dp。

显然不能枚举所有数字,超时严重。

\(dp_{i,j,k}\) 表示当前处理到第 \(i\) 位,前一位数字是 \(j\),且当前是否受到上限约束时的方案数。

对于每一位,枚举所有可能的数字,转移的时候写好约束条件就可以了,注意判断前导 \(0\)

// 记忆化搜索
#include<bits/stdc++.h>
#define int long long
using namespace std;
int dp[10][10][2],mx[10];
int dfs(int i,int j,bool pd1,bool pd2) // pd2 判断是否有前导 0
{
    if(i==-1) return 1;
    if(!pd1&&!pd2&&dp[i][j][pd1]!=-1) return dp[i][j][pd1];
    int sum=0,up=pd1?mx[i]:9;
    for(int k=0;k<=up;k++)
	{
        // 有前导 0 的情况
        if(pd2)
		{
            if(k==0) sum+=dfs(i-1,0,pd1&&(k==up),1);
            else sum+=dfs(i-1,k,pd1&&(k==up),0);
        }
        // 没有的情况
		else if(abs(j-k)>=2) sum+=dfs(i-1,k,pd1&&(k==up),0); // 约束条件
    }
    if(!pd1&&!pd2) dp[i][j][pd1]=sum;
    return sum;
}
int solve(int n)
{
	int x=0;
	while(n)
	{
		mx[x++]=n%10;
		n/=10;
	}
	memset(dp,-1,sizeof(dp));
	return dfs(x-1,0,1,1);
}
signed main()
{
    int n,m;
    memset(dp,-1,sizeof(dp));
    while(cin>>n>>m)
	{
        if(n==0&&m==0) break;
        cout<<solve(m)-solve(n-1)<<'\n'; // 同样转化为求 [1,n] 范围内的数,相减即可得到区间
    }
    return 0;
}
posted @ 2025-09-06 11:09  Aron_2023  阅读(3)  评论(0)    收藏  举报