您的位置:首页 > 编程语言 > C语言/C++

BZOJ 1026 [SCOI2009] windy数

2017-03-17 08:35 423 查看

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 。

Source

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

数位DP~

用f[i][j]表示第i位上填j时的方案数,那么f[i][j]=sum{f[i-1][k]},其中abs(j,k)>=2。

要得到[a,b]的答案,我们可以用ans[1,b]-ans[1,a-1],所以要计算的就是[1,u]的答案。

我们可以把它分为两个部分(设数u的第i位为x[i],总位数为tot):

(1)计算首位不为x[tot]的种类数,枚举位数,首位贡献的答案是f[tot][1...x[tot]-1],其余每位贡献的答案是f[i][1...9];

(2)计算首位为x[tot]的种类数,从高往低枚举,先计算完第i位<x[i]的种类数,再以第i位是x[i]为基准往下计算。这里注意,如果abs(x[i],x[i+1])<2,就不用再往下计算。

但是用上面的方法计算出来的是1~u-1的种类数,所以我们在输出的时候要输出ans[1,u-1]。

答案一定小于2*10^9,int就够了。

#include<cstdio>

int a,b,f[12][10];

int abs(int u)
{
return u>0 ? u:-u;
}

int cal(int u)
{
if(!u) return 0;
int x[12],tot=0,ans=0;
while(u) x[++tot]=u%10,u/=10;
for(int i=1;i<x[tot];i++) ans+=f[tot][i];
for(int i=tot-1;i;i--)
for(int j=1;j<=9;j++) ans+=f[i][j];
for(int i=tot-1;i;i--)
{
for(int j=0;j<x[i];j++)
if(abs(x[i+1]-j)>=2) ans+=f[i][j];
if(abs(x[i+1]-x[i])<2) break;
}
return ans;
}

int main()
{
scanf("%d%d",&a,&b);
for(int i=0;i<=9;i++) f[1][i]=1;
for(int i=1;i<=11;i++)
for(int j=0;j<=9;j++)
for(int k=0;k<=9;k++)
if(k>j+1 || j>k+1) f[i][j]+=f[i-1][k];
printf("%d\n",cal(b+1)-cal(a));
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C++ 数位DP 思路