[SCOI2009]windy数

这是本蒟蒻做的第一道数位DP。。。(大佬勿喷QVQ

这个题据说比较裸?WTF我咋不知道 QVQ

emm。。。根据题意我们可以发现,通过枚举每个的位置的数我们可以递推出满足条件的数的数量

这里用 f[i][j] 表示 有 i 位 最高位的数是 j 有f[i][j]个方案

那么我们就可以 轻松 得到DP柿子啦~~~

注意:当我们得到A B这两个数的时候,我们要对 B 和 A-1 操作。。。

  为什么A要-1呢?

  因为我们得到的windy数是从 0~B 的那么我们重复的就是 0~A-1 的windy数

接下来要处理第二步?就是判断哪些答案合法

合法答案分为三部分:

假使我们的这个数为 X 有 K 位 一个数组 S 记录每位数是什么

first:位数小于 K 必然的这些数都小于X 所以我们把 f 数组无脑加上就好

second:位数等于 K 那么我们首先可以确定,最高位小于 S[K] 的答案肯定合法

third:下面就是最难搞的地方,我们要一位一位的判断答案的合法性

  举个例子 19260817 135 我们倒着枚举每一位 for(int i=K-1;i>=1;i++);

  我们可以发现对于第 i 位的数,我们可以通过枚举这个数 j,此时要满足两个条件。

  小于 S[i] 而且 abs ( j - S[i] ) >= 2 那么选择这个数一定是合法的。

  依次类推。。。但是,如果当 S[i+1] - S[i] < 2 时 那么以后的数一定是不合法的。。。

  因为这两个数已经不满足条件了。。。

那么,我们的问题就解决了?

  NO NO NO

当我们发现每一位都合法,就是这个数本身就是一个windy数那么我们的答案就应该+1

呆码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#define ll long long
using namespace std;

ll A,B,f[15][15]; // 多少位数 最高位是多少 有多少方案 
int s[21];

inline void DP()
{
    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];
}

inline ll solve(int x)
{
    memset(s,0,sizeof(s));
    if(x==0) return 0;
    int num=0; ll ans=0;
    while(x)
    {
        s[++num]=x%10;
        x/=10;
    }
    for(int i=1;i<=num-1;i++)
        for(int j=1;j<=9;j++)
            ans+=f[i][j];
    for(int i=1;i<=s[num]-1;i++)
        ans+=f[num][i];
    
    for(int i=num-1;i>=1;i--)
    {
        for(int j=0;j<=s[i]-1;j++)
            if(abs(j-s[i+1])>=2)
                ans+=f[i][j];
        if(abs(s[i+1]-s[i])<2) break;
        if(i==1) ans+=1;
    }
    return ans;
}

int main()
{
    cin>>A>>B;
    DP();
    cout<<solve(B)-solve(A-1);
}
蒟蒻的代码,大佬别点开
原文地址:https://www.cnblogs.com/zzzyc/p/8780173.html