poj 3252 Round Numbers 数位dp

题意:给定一个区间,求区间内有多少个合法数(当这个数的二进制中0的个数>=1的个数称为合法数 二进制无前导0)

分析:很明显的数位dp。但因为是跟二进制有关,所以就要在二进制下dp。

用f[i,j,k]表示i位二进制第i位为j时有多少个有k个0的数。f[i,j,k]=f[i-1,j,k-1]+f[i-1,1-j,k-1]{j=0} f[i-1,j,k]+f[i-1,1-j,k]{j=1}

然后就是统计。

先把当前要统计的数x转换成二进制,设x转换成二进制后的位数为len。因为没有前导0,所以第一位肯定是1。然后分两步统计:

1、统计位数小于len且满足条件的数。这个直接循环一遍就好了。

2、统计位数为len且满足条件的数。那么我们从次高位开始统计,同时用一个s0记录以前统计过的位中出现了多少个0.若当前统计的位上是0则跳过,是1则k的范围是(len+1) div 2-s0到i,然后统计就好了

代码:

var
  i,j,k,x,y:longint;
  f:array[0..35,0..1,-1..35] of longint;

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

begin
  f[0,0,0]:=1;
  for i:=1 to 32 do
    for j:=0 to 1 do
      for k:=0 to i do
        if j=0
          then f[i,j,k]:=f[i-1,0,k-1]+f[i-1,1,k-1]
          else f[i,j,k]:=f[i-1,0,k]+f[i-1,1,k];
  readln(x,y);
  writeln(work(y+1)-work(x));
end.


你可能感兴趣的:(poj 3252 Round Numbers 数位dp)