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
【数据规模和约定】
20%的数据,满足 1 < = A <= B <= 1000000 。
100%的数据,满足 1 < = A <= B <= 2000000000 。
解题报告和代码都在code里:
/*
如果没有看范神的代码的话,我会因为上下界的处理写一个很繁的代码,比如昨天比赛的那几道题。经过范神代码的启发,加之一个小修改优化了一维的时间复杂度。
思路如下:
先预处理出dp,dp[i][j]表示长度为i+1,并且第一个数字为j的总数。
之后对于每组数据[a,b],用[0,b]里的总数-[1,a-1]里的总数。
所以接下来的任务就是求[0,n]里的总数,程序里find函数实现。[0,n]可以等价转化为[0,n+1),这个转化在下面可以看到其巧妙之处。
find实现过程:
find(n)求的是[0,n)里的总数
设n各位分别为a0 a1 a2... ak ( a0 > 0 )
则可以拆解为求以下几个范围的总数的和:
[ 0 0 0 0...0, a0 0 0 0... 0)
[a0 0 0 0...0, a0 a1 0 0... 0)
[a0 a1 0 0...0, a0 a1 a2 0... 0)
[a0 a1 a2 0...0, a0 a1 a2 a3... 0)
...
[a0 a1 a2 a3...0, a0 a1 a2 a3...ak)
观察每个区间,前缀固定后,后缀都是某个满区间,比如第二行,a0固定后,只用求后缀[0,a1 0 0 0..0),而这个样子又和第一行相似
比如第三行,a0,a1固定后,只用求后缀[0,a2 0 0 0...0),这个样子和第一行相似
于是乎,处理这些区间都是一个相似的过程:
由前缀推导出现在的状态(设前缀为a0 a1 a2 ... ai),然后加上dp[k-i][j],0 <= j < ai+1,j还要符合题意里的约束条件。
此时只需要两层循环就可以求出结果了。
要注意的是,某个前缀已经不符合条件时,之后的区间就不用枚举了。
另外在求第一行那个区间时,由于最高位非0的问题,不能dp[i][0]了事,而要枚举dp[i][1..9]的和。
代码如下:
*/
#include <cstdio>
#include <numeric>
#include <cstring>
using namespace std;
int dp[15][10];
int abs(int a)
{
return a<0?-a:a;
}
//dp预处理出来
void DP()
{
memset(dp,0,sizeof(dp));
for(int i = 0 ; i < 10 ; i++ )
dp[0][i] = 1;
for(int i = 1 ; i < 10 ; i++ )
for(int j = 0 ; j < 10 ; j++)
for(int k = 0 ; k < 10 ; k++)
if(abs(j-k)>1)dp[i][j]+=dp[i-1][k];
}
int find(int n)
{
int ans = 0;
char s[15];
sprintf(s,"%d",n);
int len = strlen(s);
//对于第一行区间的求解
for(int i = 0; s[i+1]; i++)
ans += accumulate(dp[i]+1,dp[i]+10,0);
//对于第二行直到最后的求解,i的条件判断是对前缀的判断,j的起点也根据是否是第一位而有所不同
for(int i = 0; s[i] && (i<2 || abs(s[i-1]-s[i-2])>1); i++)
for(int j = !i ? 1 : 0 ; j < s[i]-'0' ; j++)
ans += dp[len-i-1][j] * ( !i || (abs(j-s[i-1]+'0')>1));
return ans;
}
int main()
{
int a,b;
DP();
while(scanf("%d %d",&a,&b)==2)
{
printf("%d\n",find(b+1)-find(a));
}
return 0;
}
//此题的思路是hdu3565bi-PeakNumber的前导,这个思想只是处理了上界,同理可以拓展到处理下界
进阶题目: hdu3565 Bi-peak Number及 结题报告