2^k进制数[NOIP2006]

本文探讨使用动态规划(DP)算法解决特定进制数转换问题,详细解释了状态转移方程的推导和优化过程,重点在于理解并实现算法以解决实际问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

算法:DP

一开始这道题看得我蒙蒙的,看了半天想到了递推,无奈状态表示对了但是方程写错了。后来看了题解才发现并不是很难,一言蔽之,还是爱的不够啊~

总的来说思路还算是有的,设f[i,j]表示2^k进制数长度为i位,最高位是j的方案数,则很容易的得出f[i,j]=f[i-1,j+1]+f[i-1,j+2]+...+f[i-1,2^k-1],通过观察f[i-1,j+2]+...+f[i-1,2^k-1]不难看出,这实际上就是f[i,j+1],因此我们不必累加前面的所有那么多项的式子,原式可以化简为f[i,j]=f[i-1,j+1]+f[i,j+1]。
按照以上的那个式子,我们就可以倒序的求解了。
另外要特殊注意的是,最高位,即长度为m mod n,它的取值范围只存在与1~2^(n mod m)-1之中,而不同于其它位的长度为1~2^k-1,但是如此以来,我们会漏掉2^(m mod n)~2^k-1这些状态,而由上面的推导可知,前面的状态是由后面的状态推出来的,因此我们要过滤一下状态,对于求最高位,只有在j<=2^(m mod n)-1时才进行累加。

在优化方面,考虑用8位高精度使用的内存较高,因此采用滚动数组进行优化。

program digital;

const
 maxn=512;
 cifang:array [0..10] of longint=(1,2,4,8,16,32,64,128,256,512,1024);
 
type
 arr=array [0..50] of int64;

var
 maxlen,n,m:longint;
 f:array [0..1,0..maxn] of arr;
 ans:arr;
 
procedure init;
var
 i:longint;
begin
 readln(n,m);
 maxlen:=m div n;
 for i:=cifang[n]-1 downto 1 do
  begin
   f[0,i,0]:=1;
   f[0,i,1]:=1;
  end;
 ans[0]:=1;
 ans[1]:=0;
end;

function HPP(x,y:arr):arr;
var
 i,len,t:longint;
begin
 t:=0;
 fillchar(HPP,sizeof(HPP),0);
 if x[0]>=y[0] then len:=x[0] else len:=y[0];
 HPP[0]:=len; 
 for i:=1 to len do 
  begin
   HPP[i]:=x[i]+y[i]+t;
   t:=HPP[i] div 1000000000;
   HPP[i]:=HPP[i] mod 1000000000;
  end;
 if t>0 then
  begin
   inc(HPP[0]);
   HPP[HPP[0]]:=t;
  end;
 while (HPP[HPP[0]]=0) and (HPP[0]>1) do dec(HPP[0]); 
end;

procedure main;
var
 i,j,t:longint;
begin
 {先处理不是最高位的位置。}
 for i:=1 to maxlen-1 do
  begin
   t:=i mod 2;
   for j:=cifang[n]-1 downto 1 do
    begin
     f[t,j]:=HPP(f[1-t,j+1],f[t,j+1]);
     ans:=HPP(ans,f[t,j]);
    end;
  end;
 t:=maxlen mod 2;
 {处理最高位+特殊限制。}
 if m mod n>0 then
  begin
   for j:=cifang[n]-1 downto 1 do
    begin
     f[t,j]:=HPP(f[1-t,j+1],f[t,j+1]);
     if j<=cifang[m mod n]-1 then ans:=HPP(ans,f[t,j]);   
    end;
  end;
end;

procedure print(x:arr);
var
 i:longint;
begin
 write(x[x[0]]);
 for i:=x[0]-1 downto 1 do
  begin
   if x[i]<100000000 then write(0);
   if x[i]<10000000 then write(0);
   if x[i]<1000000 then write(0);
   if x[i]<100000 then write(0);
   if x[i]<10000 then write(0);
   if x[i]<1000 then write(0);
   if x[i]<100 then write(0);
   if x[i]<10 then write(0);
   write(x[i]);
  end;
 writeln;
end;

begin
 assign(input,'digital.in'); reset(input);
 assign(output,'digital.out'); rewrite(output);
 
 init;
 main;
 print(ans);
 
 close(input); close(output);
end.



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值