嘘~ 正在从服务器偷取页面 . . .

洛谷P2657 [SCOI2009] windy 数 题解


洛谷P2657 [SCOI2009] windy 数 题解

题目链接:P2657 [SCOI2009] windy 数

题意

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

对于全部的测试点,保证 \(1 \leq a \leq b \leq 2 \times 10^9\)

常规的数位dp,只需要记录上一位的数字即可

但是注意,最高位是不需要限制的

具体的,我们是从高位往低位做dp的

至于为什么高位往低位做dp,可以看下面的解释

因为低位开始会导致无法判断是否超过了当前的限制

例如 \(4234\) ,假设我们从高位开始做

如果以 \(4\) 开始,可以发现接下来一位不能超过 \(3\)

如果用 \(0 \sim 3\) ,则接下来没有任何限制

但是如果我们从低位开始做,比如填了一个 \(8\)

那么最后我们有可能推出来个 \(4238\) 之类的

因为无法判断

回到刚刚的问题,我们从高位往低位做数位dp

那么第一位,也就是我们推的某个数的最高位,

是可以随便填而不受到大于 \(2\) 那个限制的(即前导零不能限制实际的最高位选择)

因此我们还需要记录是否之前全是 \(0\)

复杂度不太清楚,不过应该和位数有关,这题的位数就十几,所以基本上是 \(O(1)\)

代码:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iomanip>
#include <random>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)()

int n,a[15],num[15],f[15][15];
int dfs(int u,int pre,int st,int limit)
{
    if(!u) return 1;
    if(!limit&&f[u][pre]!=-1)
        return f[u][pre];
    int up=limit?num[u]:9,ans=0;
    for(int i=0; i<=up; i++)
    {
        if(abs(i-pre)<2) continue;
        if(st&&!i) ans+=dfs(u-1,-2,1,limit&&num[u]==i);
        else ans+=dfs(u-1,i,0,limit&&num[u]==i);
    }
    if(!limit&&!st) f[u][pre]=ans;
    return ans;
}
int solve(int x)
{
    int len=0;
    while(x>0)
    {
        num[++len]=x%10;
        x/=10;
    }
    return dfs(len,-2,1,1);
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    memset(f,-1,sizeof(f));
    int l,r; cin >> l >> r;
    cout << solve(r)-solve(l-1) << '\n';
    return 0;
}

文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录