首先根据题目的要求初始化
一般为:
for(int i = 0;i <= 1;i ++) dp[1][i] = 1;
for(int i = 1;i <= 最大数的位数;i ++)
for(int j = 0;j <= 9;j ++)
for(int k = 0;k <= 9;k ++)
if(......)//题目要求
dp[i][j] += dp[i-1][k];
dp[i][j]表示不大于位数为i且最高位为j的方案总数。
但是对于最高位,我们并不总是能取得以它为开头的全集(19999……这一类除外)
不过,如果去掉最高位,我们所枚举的数都是合法的。所以可以直接枚举每一位1 ~ 9的答案就好了。
对于其他的数如果我们没有方法直接统计,或许我们要想办法把他拆开。
举个例子,对于 24653这组数。
因为我们有5位数,所以对于前四位是一定会包含的。
所以我们枚举之前每一位数以1~9为最高位的答案(若不允许含前导0)。
在这里,对于最高位,我们只加上以1位最高位的答案。(因为以2为最高位并不能枚举到所有的数)
然后我们假定最高位为2,加上次高位以0~3为下一位的答案。
然后我们假定前面的数为24,……
……
然后对于最后一位的3我们怎么处理,难道这个3不要了?
那就不要了吧,写开区间吧,我们求24654这个数。
对了,有一个细节,如果对于某一枚举的数位自身已经不符合要求了。
由于我们的每一位都会带到下一位,所以后面的一定不符合条件,要直接跳出。
例如我们要求不大于某一个数中相邻两个数不大于2的个数,对于12347666这个数如果我们已经假定前几
位分别是12347,那么对于后面的数无论我们选什么都是不成立的,所以直接break;
附上windy数的代码及题目链接:
http://www.lydsy.com/JudgeOnline/problem.php?id=1026
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int a,b,ans,cnt,jl[105],dp[105][105];
void init()//数位dp的套路,按照题目要求初始化。
{
for(int i = 0;i <= 9;i ++) dp[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)
dp[i][j] += dp[i-1][k];
}
int tj(int x)//这是一个开区间的。
{
cnt = 0;ans = 0;
while(x) {jl[++cnt] = x % 10;x /= 10;}//拆分x
for(int i = 1;i < cnt;i ++)
for(int j = 1;j <= 9;j ++)
ans += dp[i][j];//如果去掉最高位,我们所枚举的数都是合法的。
for(int i = 1;i < jl[cnt];i ++) ans += dp[cnt][i];//最高位到第jl[cnt-1]位为止还是全集。
for(int i = cnt - 1;i >= 1;i --)//假设上一位已经确定为jl[i+1]。
{
//由于我们记录的都是全集而实际某些数不一定能达到,为了保证达到,最高位不枚举自身。
//对于最高位,我们就假设我们确定了,而在下一位枚举它。
for(int j = 0;j < jl[i];j ++)
if(abs(jl[i+1]-j) >= 2)
ans += dp[i][j];
//如果这个数字已经有两位矛盾了,后面的再枚举也是不对的。
if(abs(jl[i+1]-jl[i]) < 2) break;
}
return ans;
}
int main()
{
init();
scanf("%d%d",&a,&b);
cout<<tj(b+1) - tj(a);
}