51Nod-1946-特殊表示法

235 篇文章 1 订阅
182 篇文章 3 订阅

ACM模版

描述

描述

题解

首先我们来分析一下斐波那契数列的基本性质,众所周知,斐波那契数列从第二项开始后,能够组合(每一项只有选与不选两种操作)出来任意自然数,所以才会有这个特殊表示法的存在,并且这个表示法里不存在任意两个连着的 1 ,因为一旦存在就可以转化为另一个数,毕竟 fib[i]=fib[i1]+fib[i2],如果 i1 项和 i2 项都选了,那就可以转化为 i 项。

至此,我们来考虑两个序列叠加在一起后可能存在的情况。首先 A B 都用特殊表示法表示,叠加在一起,则可能存在某一下出现 012 次,我们需要考虑将大于 1 的和连着两个 1 的转化一下,大于 1 的我们可以转化为两个一个大一些的和一个小一些的项之和,而连着两个 1 的则可以转化为更大的一项。对于前者我们逆着来,后者我们正着来,就差不多了。

但是此时提交还会有 bug ,这也是这道题 AC 率那么低的原因,有些地方不容易想到,下载数据一看会发现自己漏考虑情况了。不容易想到的地方大致有两点,一是存在大于 2 的情况发生,这个是因为当我们在进行第一个转化过程时,会有加项操作,所以会出现大于 2 的情况,其次是边界问题需要考虑一下,具体的看一下代码吧,代码中注释写得十分清楚了。

可以加上输入输出外挂优化一下,我比较懒,只是将输出部分先存在字符串然后统一输出了一下,节约了一些时间,输入的时候也可以整行读入然后处理一下,比较容易,但是架不住我懒。

代码

#include <iostream>
#include <cstdio>

using namespace std;

const int MAXN = 1e6 + 5;

int n, m;
int a[MAXN];
int b[MAXN];
char c[MAXN << 1];

int main()
{
    scanf("%d", &n);
    getchar();

    for (int i = 0; i < n; i++)
    {
        a[i] = getchar() - '0';
        getchar();
    }

    scanf("%d", &m);
    getchar();

    for (int i = 0; i < m; i++)
    {
        a[i] += getchar() - '0';
        getchar();
    }

    //  逆着扩展一遍
    n = max(m, n);
    for (int i = n - 1; i > 1; i--)
    {
        if (a[i] >= 2)
        {
            //  ex: 2 * 5 = 8 + 2
            a[i] -= 2;
            a[i - 2] += 1;
            a[i + 1] += 1;
            n = max(i + 2, n);
        }
        if (a[i] >= 1 && a[i - 1] >= 1)
        {
            //  ex: 2 + 3 = 5
            a[i] -= 1;
            a[i - 1] -= 1;
            a[i + 1] += 1;
            n = max(i + 2, n);
        }
    }

    //  处理边界特殊情况
    if (a[0] >= 2 && a[1] == 0)
    {
        //  ex: 1 + 1 = 2
        a[0] -= 2;
        a[1] += 1;
    }
    else if (a[1] == 3)
    {
        //  ex: 2 + 2 + 2 = 1 + 2 + 3
        a[1] -= 1;
        a[0] += 1;
        a[2] += 1;
    }

    //  正着扩展若干遍,一直到无法扩展
    int flag = 1;
    while (flag)
    {
        flag = 0;
        for (int i = 0; i < n; i++)
        {
            if (a[i] >= 1 && a[i + 1] >= 1 && a[i + 2] != 1)
            {
                flag = 1;
                //  ex: 2 + 3 = 5
                a[i] -= 1;
                a[i + 1] -= 1;
                a[i + 2] += 1;
                n = max(i + 3, n);
            }
        }
    }

    int cnt = 0;
    for (int i = 0; i < n; i++)
    {
        c[cnt++] = a[i] + '0';
        if (i != n - 1)
        {
            c[cnt++] = ' ';
        }
    }
    c[cnt] = '\n';

    printf("%d\n%s", n, c);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值