BZOJ 1026 [SCOI2009]windy数 【数位Dp】

BZOJ 1026  [SCOI2009]windy数

Description

  windy定义了一种windy数。不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。 windy想知道,
在A和B之间,包括A和B,总共有多少个windy数?

Input

  包含两个整数,A B。

Output

  一个整数

Sample Input

【输入样例一】
1 10
【输入样例二】
25 50

Sample Output

【输出样例一】
9
【输出样例二】
20

HINT

 

【数据规模和约定】

100%的数据,满足 1 <= A <= B <= 2000000000 。

 

题解:

数位dp模板题了吧。为什么那么多人喜欢写递推,唯独我钟爱记忆化,多简单直白,还有板子。。。

不多说了,这个问题比较简单。对于一个区间,可以用前缀和求值。

我们就只考虑怎么求 solve(n)。

按位 dp,从高到低位,首先先判断:如果没有前导 0 和 限制条件,并且这个 dp 状态之前已经存过,则直接返回值。

接着利用 限制条件确定这位的最大值,枚举。

这里首先要判断一下,是否有前导0,因为相邻两位差要 >=2,不特判的话,1,2这两个值会忽略,造成结果错误。

然后就继续递归。

最后如果没有前导 0 而且没有限制条件,存下当前 Dp 状态,方便之后记忆化,提高效率。

 

上代码:

 

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int l,r,ans,a[20],len;
 4 int dp[20][15];
 5 inline int abs(int x)
 6 {
 7     return x>0?x:-x;
 8 }
 9 inline int dfs(int pos,int num,int sum,bool lead,bool limit)
10 {
11     sum=0;
12     if (pos==0) return 1;
13     if (!lead && !limit && dp[pos][num]!=-1) return dp[pos][num];
14     int up=limit?a[pos]:9;
15     for (int i=0; i<=up; i++)
16     {
17         if (lead) sum+=dfs(pos-1,i,sum,i==0,limit && i==up);
18         else if (abs(i-num)>=2) sum+=dfs(pos-1,i,sum,0,limit && i==up);
19     }
20     if (!lead && !limit) return dp[pos][num]=sum; else return sum;
21 }
22 int solve(int x)
23 {
24     len=0; memset(dp,-1,sizeof(dp));
25     while (x)
26     {
27         a[++len]=x%10;
28         x/=10;
29     }
30     return dfs(len,0,0,1,1);
31 }
32 int main()
33 {
34     scanf("%d%d",&l,&r);
35     printf("%d",solve(r)-solve(l-1));    
36     return 0;
37 }
View Code

 

 

数位dp 还是挺重要的,一定要掌握熟练哦,多练题。

 

 

加油加油加油!!!fighting fighting fighting!!!

 

posted on 2018-07-19 15:35  Frank-King  阅读(183)  评论(0编辑  收藏  举报