洛谷P2657 Windy数 (数位DP)

洛谷P2657 Windy数 (数位DP)

https://www.luogu.com.cn/problem/P2657

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

思路:

数位DP

首先我们用前缀和的思想让ans(a,b)转换成ans(0,b)-ans(0,a-1)

那么如何求解某个值到0有多少windy数嘞?

我们观察一下,如3421,我们先全考虑有前导0

对于 第一位3 而言,当我们取它为2的时候,即0000 - 2999时

数的枚举范围是确定的,非首位均是0->9,而首位是0->(num-1)

这块很容易列出状态转移方程进行求解,这种全填充型的可以预处理得到的

(开一个二维数组,分别记录位置以及首数字)

而当首位取了3,我们不难发现它的答案实质上与3199是一样

也就是说,我们在考虑3?...的时候,会出现两类情况

1)首位后一位 " ?"首位 构成非法关系

那么这里显然就像上面的例子一样,我们只要考虑小于" ?",且与首位合法的情况即可,直接利用前面预处理得到的全填充型

2)首位后一位 " ?"首位 构成合法关系

那么其实这一步也就是取了首位后一位,再把这个结果继承给 当前的首位,再去算小于且合法的情况即可

进行完上面的过程后我们开始要考虑处理前导0的情况

我们在上面DP的过程中,把01..以及00...这种情况筛掉了,即前导0不应与首位形成非法关系

所有我们要把这部分的补回来,值得注意的是0000000这种,等价是0(补回0要特判下)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define INF 0x3f3f3f3f
#define maxn 15
#define minn -105
#define ll long long int
#define ull unsigned long long int
#define uint unsigned int
inline int read()
{
    int ans=0;
    char last=' ',ch=getchar();
    while(ch<'0'|ch>'9')last=ch,ch=getchar();
    while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
    if(last=='-')ans=-ans;
    return ans;
}
int unlimit[13][10];

int cal(int cur)
{
    if(cur<10)return cur;
    int pre=12,num=0,pos=0;
    int limit=1;
    while(1)
    {
        if(!cur)break;
        num=cur%10;
        cur/=10;
        pos++;

        if(abs(pre-num)<=1)limit=0;

        for(int i=0;i<min(10,pre);i++)
        {
            if(abs(i-num)>1)limit+=unlimit[pos-1][i];
        }
        pre=num;
    }
    //cout<<limit<<endl;
    for(int i=0;i<num;i++)
    {
        limit+=unlimit[pos][i];
    }
    while((pos--)>=1)
    {
        limit+=unlimit[pos][1];
        if(pos>1)limit+=unlimit[pos][0];
    }
    //cout<<limit<<endl;
    return limit;
}

int main()
{
    int a,b;
    for(int i=0;i<10;i++)
        unlimit[1][i]=1;

    for(int pos=2;pos<11;pos++)
    {
        for(int i=0;i<10;i++)
        {
            for(int j=0;j<10;j++)
               if(abs(i-j)>1) unlimit[pos][i]+=unlimit[pos-1][j];
            //cout<<pos<<"   "<<i<<"  "<<  unlimit[pos][i]<<endl;
        }
    }
    cin>>a>>b;
    cout<<cal(b)-cal(a-1)<<"\n";
    return 0;
}




posted @ 2020-06-30 21:01  et3_tsy  阅读(128)  评论(0编辑  收藏  举报