您的位置:首页 > 其它

51Nod-1946-特殊表示法

2018-01-01 20:28 176 查看
ACM模版

描述



题解

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

至此,我们来考虑两个序列叠加在一起后可能存在的情况。首先 A 和 B 都用特殊表示法表示,叠加在一起,则可能存在某一下出现 0、1、2 次,我们需要考虑将大于 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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数学相关