洛谷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;
}