1. 程式人生 > >51Nod-1946-特殊表示法

51Nod-1946-特殊表示法

ACM模版

描述

描述

題解

首先我們來分析一下斐波那契數列的基本性質,眾所周知,斐波那契數列從第二項開始後,能夠組合(每一項只有選與不選兩種操作)出來任意自然數,所以才會有這個特殊表示法的存在,並且這個表示法裡不存在任意兩個連著的 1,因為一旦存在就可以轉化為另一個數,畢竟 fib[i]=fib[i1]+fib[i2],如果 i1 項和 i2 項都選了,那就可以轉化為 i 項。

至此,我們來考慮兩個序列疊加在一起後可能存在的情況。首先 AB 都用特殊表示法表示,疊加在一起,則可能存在某一下出現 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; }