jzoj P3769 A+B___dfs+规律

83 篇文章 0 订阅
78 篇文章 0 订阅

题目大意:

对于每个数字x,我们总可以把它表示成一些斐波拉切数字之和,比如8 = 5 + 3, 而22 = 21 + 1,因此我们可以写成 x = a1 * Fib1 + a2 * Fib2 + a3 * Fib3 + … + an * Fibn, 其中,Fib1 = 1, Fib2 = 2…. Fib[i] = Fib[i – 1] + Fib[I - 2], 且a[n] > 0.那么我们称ai为x的一种斐波拉切表示,由于表示方法有很多种,我们要求最大化a[1…n],即,如果b[1…n]和a[1…m]都可以表示x,若m > n 则a更大,若 m = n, 则从高位到低位比,第一个不同处i,若ai > bi 则a比b大。
你的任务很简单,给你两个用斐波拉切数最大化表示的两个数字,输出他们相加后用斐波那契最大化表示的数字。

对于30%的数据 长度 <= 1000
对于100%的数据 长度 <= 1000000

题解:

这题其实特别的神奇…
是需要找规律的,当然对于直接高精度的dalao们,我当然是膜拜辣。。
我们先列出一副符合题目的斐波那契数列(从1,2开始):
序1 2 3 4 5 6
fi 1 2 3 5 8 13
A 0 1 0 1
B 0 1 0 0 1
C 0 2 0 1 1
我们考虑C=A+B的情况
此时的C如果化简,我们发现任意一个ai必定为0或1!这个在草稿纸上随便推一下应该都知道的..
对于C上某一位:
①>=1,则我们可以发现,
因为一个直观的斐波那契数列公式是
fi=fi-1+fi-2]
则当f[i]>=1且f[i-1]>=1时我们直接将能使用的对数k递归到f[i+1]去重复进行①②操作

可知k=min{fi,fi-1}

②>=2,则我们推演一下:
F[i-1]+F[i]=F[i+1]
F[i-1]+F[i-2]+F[i]=F[i+1]+F[i-2]
2F[i]=F[i+1]+F[i-2]
那我们可以得出,每2个fi就可以【分解成】1个f[i-2]和一个f[i+1],此时我们能分多少对就分多少对过去然后递归过去,然后重复进行①②操作

代码:

var
        ans,a,b:array [-100..1000100] of longint;
        i,j,k,n,m,z:longint;

procedure dfs(dep,x:longint);
var
        k:longint;
begin
        ans[dep]:=ans[dep]+x;
        if ans[dep-1]>=1 then
            if ans[dep]>=1 then
               begin
                   if ans[dep-1]>ans[dep]
                      then k:=ans[dep]
                      else k:=ans[dep-1];
                      ans[dep]:=ans[dep]-k;
                      ans[dep-1]:=ans[dep-1]-k;
                      dfs(dep+1,k);
               end;
        if ans[dep]>=2 then
           begin
                      k:=ans[dep] div 2;
                      ans[dep]:=ans[dep] mod 2;
                      if dep<=2
                         then dfs(dep-1,k)
                         else dfs(dep-2,k);
                      dfs(dep+1,k);
           end;



end;

begin
        read(n);
        for i:=1 to n do read(a[i]);
        readln;
        read(m);
        for j:=1 to m do read(b[j]);
        if n>m then z:=n
               else z:=m;
        z:=z+50;
        for i:=1 to z do
            ans[i]:=a[i]+b[i];
        for i:=1 to z do
            begin
                     if ans[i-1]>=1 then
                        if ans[i]>=1 then
                           begin
                                if ans[i-1]>ans[i]
                                   then k:=ans[i]
                                   else k:=ans[i-1];
                                ans[i]:=ans[i]-k;
                                ans[i-1]:=ans[i-1]-k;
                                dfs(i+1,k);
                          end;
                     if ans[i]>=2 then
                        begin
                             k:=ans[i] div 2;
                             ans[i]:=ans[i] mod 2;
                             if i<=2
                                then dfs(i-1,k)
                                else dfs(i-2,k);
                             dfs(i+1,k);
                        end;
            end;
        while ans[z]=0 do dec(z);
        write(z,' ');
        for i:=1 to z do write(ans[i],' ');
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值