[BZOJ1026][SCOI2009]windy数 解题报告|数位dp

Description

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

 

  一直还是有点怕数位DP的...包括今天做这道简单的小题也花了很久的时间处理细节。

  首先大体的思路非常明显,定义一个DP f[i,j]表示第i位放数字j有多少种方法,可以通过前一位的一些满足的数字推出这一位。

  但是如何来解决在某个数A的范围内呢...?

  并且一旦前面的没有取满,这一位都是可以0..9任意取的

  并且还要考虑以这一位为开头的情况

  没有前导零,也就是说当这一位为0的时候是不能作为开头的。

  思考了一会儿,想出了一种方案。f[i,j]表示第i为放数字j并且从1~i并排除取到原数的方案数

  那么通过f[i-1]然后枚举0~9就可以先得出初步的f[i](因为i-1位以前都没有取到满了,这一位随便怎么取都不会超过原数

  第二部分就是当前数为起点,那么我们枚举1~9,inc(f[i][j])就可以了

  还有一种情况,就是i-1位已经取满了,当前这位只能取0~num[i]这些数(num[i]表示原数在第i位的数字)

  但是我们只能枚举到num[i]-1,因为要维护f[i]这个数组的性质:没有取到满

  注意细节:第三种情况能够转移当且仅当1~i-1位都满足windy数的性质 (这里我们可以用一个bool类型标记)

  处理完之后再判断1~i是否满足windy数的性质

  f[最后一位][0..9]就是答案。

  其实还没有结束...别忘了原数,如果那个bool类型到最后还是为真,说明原数也是一个windy数

  但是显然我们在f数组里是不会统计到原数的,这个时候还要答案+1

  最后还有一个细节,就是特判0的情况,虽然题目保证>=1但是我们要的答案是solve(r)-solve(l-1),还是会即算到0的情况

  要特判solve(0)=0

  前几天写惯了树剖今天几道小题真是爽啊...

  

 1 /**************************************************************
 2     Problem: 1026
 3     User: mjy0724
 4     Language: Pascal
 5     Result: Accepted
 6     Time:0 ms
 7     Memory:228 kb
 8 ****************************************************************/
 9  
10 program bzoj1026;
11 var i,l,r:longint;
12     w,num:array[-1..15]of longint;
13     f:array[-1..15,0..9]of longint;
14  
15 function solve(p:longint):longint;
16 var i,j,k,ans:longint;
17     flag:boolean;
18 begin
19         if p=0 then exit(0);
20     fillchar(f,sizeof(f),0);
21     for i:=9 downto 1 do if p div w[i]>0 then break;
22         if p div w[i]>0 then inc(i);
23     for j:=i downto 1 do num[j]:=p div w[j-1] mod 10;
24         for j:=1 to num[i]-1 do f[i,j]:=1;
25     flag:=true;
26     for i:=i-1 downto 1 do
27     begin
28         for j:=0 to 9 do
29             for k:=0 to 9 do if abs(j-k)>=2 then inc(f[i,j],f[i+1,k]);
30         for j:=1 to 9 do inc(f[i,j]);
31         if flag then for j:=0 to num[i]-1 do if abs(j-num[i+1])>=2 then inc(f[i,j]);
32         if abs(num[i]-num[i+1])<2 then flag:=false;
33     end;
34     ans:=0;
35     for i:=0 to 9 do inc(ans,f[1,i]);
36         if flag then inc(ans);
37     exit(ans);
38 end;
39  
40  
41 begin
42     w[0]:=1;
43     for i:=1 to 9 do w[i]:=w[i-1]*10;
44         readln(l,r);
45     writeln(solve(r)-solve(l-1));
46 end.

 

   

 

posted @ 2015-04-13 11:12  mjy0724  阅读(...)  评论(... 编辑 收藏