windy数
看到一些dalao写的不浅显易懂,我来补一发较为详细的代码
思想
- 1.有前缀和的思想
即,我们在\(b\)那个位置,求出一个1到\(b\)区间的数目
然后在 \(a-1\)那里,进行同样的操作,进行运算,
最后的答案就是solve(b) - solve(a-1)
- 2.这道题是一个数位DP
通常数字都很大,我们可以把他们存到一个数组里,这样访问的时候,访问数组里的数就好了,很方便的(详见代码)
- 3.我们拆分完成后,就到了展示高端操作的时候
蒟蒻觉得那些dalao写的过于抽象,便使用了记忆化搜索
(其实记忆化搜索与循环嵌套做出来的dp时间复杂度是一样的,只不过,记忆化搜素可能会比较直观,但代码量长一些)
先把这个dfs的代码贴出来
dfs中有四个参数
pos表示当前搜索到哪一位
pre表示前驱,即自己的上一位是谁
flg表示当前所搜到的这种情况,其数字是否能够与原先的数匹配上
举个例子,一个数123456,我们枚举到1234,这是的flg就为1
zero的含义有两种,是相反的,我都有AC代码,大家可以去看
第一种是zero为1,表示没有前导零,反过来,zero为零,表示有前导零(我下面的dfs就是用的这个含义,刚刚进入的时候,为dfs(当前num的位数,0, 1, 0) )
第二种是zero为0,表示有前导零,反过来,zero为0,表示没有前导零,这时“初代”dfs的参数分别为(当前num的位数,0, 1, 1),当然二者还有一些细节在代码中
\(f[i][j]\)表示搜到第\(i\)位,在前驱是\(j\)的情况下,的最多方案数目
inline int dfs (int pos, int pre, int flg, int zero) {
if (!pos) return 1;//我位数为零了,自然就return了
//有同学要问了,为什么pos等于零了,我们就可以返回1?
//我们的dfs每一次搜索,都是在保证合法的情况下
//如果某种情况不合法,是根本不会进入到pos为零这个阶段
if (!flg && f[pos][pre]!=-1&&zero) return f[pos][pre];
//典型的记忆化,如果f数组以前更新过,且当前没有前导零,我们下一次拿来直接用就好了
int net, tot(0);
/*
tot是来记录答案的,net是来抉择自己下一个可以选成什么
这里有这么一个坑点,假如原数是123456
我们当前搜到123,那继续搜的话,后三位是不是一定要小于456?
因为我们不可能比原数还要大对吧
而假如我们搜到122,那后面我们即使是999,是不是依旧小于原数123456?
(姑且不考虑搜到的这个情况是否合法)
来看代码,当flg等于1,意味着前边的数,和自己已经搜到的一样,
那么我们下一位可选的最大数是不是就是原数中的那一位?
同样,我们举个例子(都是在默认原数是123456的前提下)
当前搜到123,发现flg等于一(因为123456的前三位与123的前三位是相匹配的)
那我们的net(上限)最大为原数中的那一位,在这里是4
如果当前搜到122,发现flg等于零,
意味着自己与原数不一样
那既然不一样,那一定是之前的某个高位比原数小,
既然高位都比原数小了,后边无论怎么操作,都不会大于原数
而每一位数字的上限是9,我们给它定位最大值9就可以了
*/
if (flg) net = num[pos];
else net = 9;
for (int i = 0; i <= net; ++ i) {
int cha = abs (pre-i);
//找差值,判断
if (cha<2 && zero);
/*注意我这里有一个分号
即if (cha<2 && zero)成立之后,不会进行任何操作
这个if是说,差值小于2,并且前边没有前导零
判断有没有前导零的判断很关键
我们假定搜到一个数1 其实能这个1实际上是“00001”(前面具体多少个零我也不知道)
而我们来看 501
抛开这个if不说,我们知道501不是windy数,而1是.那我们回归到if上
001和501同时满足cha < 2,但是对于001却不满足zero为1的情况,所以001不会进这个if而501会进入,501就被“过滤”了
这是最简单的一种情况
*/
else if (i == net&&flg) tot += dfs (pos-1, i, 1, 1);
/*
if (i==net&&flg)
这是在判断,下一位要选的数是不是到达了上限,且是否与原数匹配
如果发现已经到了上限(我们姑且不论那个上限是9还是原数中的某一位)
那我们可以继续搜下一层,位数要减1,前驱即为i,flg为1,
而最后的参数zero为什么会是1呢?
大家来想一下,首先我们的原数(最开始被拆分的那个数)非零
而我们现在枚举到的这个数,能与原数匹配
那么这个被枚举(或者叫“搜索”)到的数,就一定也非零
自然,zero为1
(zero的含义为第一种,不清楚的自己去上面扒拉)
*/
else {
if (i!=0 || zero) tot += dfs (pos-1, i, 0, 1);
/*
这个if中是“或”的条件
即只要满足当前枚举到的下一位不是0,或者zero为1(即表示没有前导零)就可以继续跑dfs
这里有个问题,flg为什么是0.大家来考虑i这个循环
i是从1循环到上限net的,我们前边提到过,net只能是9或者原数中的一位
假如net为9,那么我们的flg已经是0了,
因为net等于9其成立条件就是flg为零
假如net为原数中一位,那假如我们当前枚举的下一位不是原数中的那个数(上限)(net)
那我们自然是flg为0,假如到了上限,并且flg前面是1,(这是上一个if干的活了)
*/
else tot += dfs (pos-1, i, 0, 0);
/*
这个else成立的条有两个
第一个,i不等于0;第二个,zero是零
就是说,当前要枚举的下一位是0,并且自己前边是有前导零的,所以dfs的参数zero才为0
*/
}
}
if (!flg && zero) f[pos][pre] = tot;
//保存答案,方便以后使用
//这里对应上面的记忆化,在一定条件下时记录,保证一致性
return tot;
}
下面贴一下完整代码
zero第一种含义的代码
/*
By TheStars
Time:2020.03.09
*/
#include <bits/stdc++.h>
#define N 330
using namespace std;
int a, b, cnt;
int num[N];
int f[N][N];
inline int dfs (int pos, int pre, int flg, int zero) {
if (!pos) return 1;
if (!flg && f[pos][pre]!=-1&&zero) return f[pos][pre];
int net, tot(0);
if (flg) net = num[pos];
else net = 9;
for (int i = 0; i <= net; ++ i) {
int cha = abs (pre-i);
if (cha<2 && zero);
else if (i == net&&flg) tot += dfs (pos-1, i, 1, 1);
else {
if (i!=0 || zero) tot += dfs (pos-1, i, 0, 1);
else tot += dfs (pos-1, i, 0, 0);
}
}
if (!flg && zero) f[pos][pre] = tot;
return tot;
}
inline int work (int x) {
memset (f, -1, sizeof f);
cnt = 0;
while (x) num[++cnt] = x%10, x /= 10;
return dfs (cnt, 0, 1, 0);
}
int main (){
scanf ("%d%d", &a, &b);
int one = work (b);
int two = work (a-1);
cout << one - two;
fclose (stdin), fclose (stdout);
return 0;
}
zero第二种含义的代码
/*
By TheStars
Time:2020.03.09
*/
#include <bits/stdc++.h>
#define N 330
using namespace std;
int a, b, cnt;
int num[N];
int f[N][N];
inline int dfs (int pos, int pre, int flg, int zero) {
if (!pos) return 1;
if (!flg && f[pos][pre]!=-1&&!zero) return f[pos][pre];
int net, tot(0);
if (flg) net = num[pos];
else net = 9;
for (int i = 0; i <= net; ++ i) {
int cha = abs (pre-i);
if (cha<2 && !zero);
else if (i == net&&flg) tot += dfs (pos-1, i, 1, 0);
else {
if (i!=0 || !zero) tot += dfs (pos-1, i, 0, 0);
else tot += dfs (pos-1, i, 0, 1);
}
}
if (!flg && !zero) f[pos][pre] = tot;
return tot;
}
inline int work (int x) {
memset (f, -1, sizeof f);
cnt = 0;
while (x) num[++cnt] = x%10, x /= 10;
return dfs (cnt, 0, 1, 1);
}
int main (){
scanf ("%d%d", &a, &b);
int one = work (b);
int two = work (a-1);
cout << one - two;
fclose (stdin), fclose (stdout);
return 0;
}

浙公网安备 33010602011771号