bzoj 1026: [SCOI2009]windy数 数位dp

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

这题很容易就想到数位dp的方法,把求区间转换成求1-A和1-B之间的wendy数。用f[i,j]表示有i位的数且最高位为j时有多少windy数。f[i,j]=sum(f[i-1,k]){abs(j-k)>1}。

关键在于统计。设当前统计的数x的位数为len。

因为不能有前导0,所以就分三步来统计:1、统计位数小于len的wendy数。2、统计位数为len且最高位小于x的windy数。3、再从次高位到个位逐位按照类似步骤(2)的方法统计即可。为了避免前导零的情况,最高位统计时不能将f[i,0]算进去。

代码:

var
  i,j,k,n,m:longint;
  f:array[1..20,0..9] of longint;

function work(x:longint):longint;
var
  a1,i,j:longint;
  a:array[1..30] of longint;
begin
  a1:=0;
  while x>0 do
  begin
    inc(a1);
    a[a1]:=x mod 10;
    x:=x div 10;
  end;
  a[a1+1]:=100;
  work:=0;
  for i:=a1-1 downto 1 do
  begin
    for j:=0 to a[i]-1 do
      if abs(j-a[i+1])>=2 then work:=work+f[i,j];
    if abs(a[i]-a[i+1])<2 then break;
  end;
  for i:=1 to a[a1]-1 do
    work:=work+f[a1,i];
  for i:=a1-1 downto 1 do
    for j:=1 to 9 do
      work:=work+f[i,j];
end;

begin
  for i:=0 to 9 do
    f[1,i]:=1;
  for i:=2 to 10 do
    for j:=0 to 9 do
      for k:=0 to 9 do
        if abs(j-k)>=2 then f[i,j]:=f[i,j]+f[i-1,k];
  readln(n,m);
  writeln(work(m+1)-work(n));
end.


你可能感兴趣的:(bzoj 1026: [SCOI2009]windy数 数位dp)