📌  相关文章
📜  数组的字典序最大排列,使得 a[i] = a[i-1] + gcd(a[i-1], a[i-2])(1)

📅  最后修改于: 2023-12-03 15:10:22.756000             🧑  作者: Mango

数组的字典序最大排列
问题描述

给定一个长度为 $n$ 的数组 $a$,如果 $i \geq 2$,则 $a_i$ 可以写成 $a_{i-1} + \gcd(a_{i-1}, a_{i-2})$ 的形式。求字典序最大的 $a$ 数组。

解决方案

首先考虑到 $a_i$ 是由前两个数 $a_{i-1}$ 和 $a_{i-2}$ 来得到的,而且 $\gcd$ 表示的是最大公约数,所以很容易想到贪心算法。

我们可以从小到大枚举 $a$ 数组中的值,对于每一个 $a_i$ ,将 $a_{i-1}$ 的值枚举从大到小,找到满足 $a_i = a_{i-1} + \gcd(a_{i-1}, a_{i-2})$ 的最大值 $a_{i-1}$ ,然后将 $a_{i-2}$ 更新为 $a_{i-1}$,继续往后推。

在更新 $a_i$ 的过程中,我们可以用一个桶来存储每个数出现的次数,这样就可以方便地找到下一个最大的 $a_{i-1}$。

代码实现

这里给出 C++ 代码实现。

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;

const int MAXN = 100005;

int n;
int a[MAXN], t[MAXN];

int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }

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

    memset(t, 0, sizeof(t));
    for (int i = 1; i <= n; i++) {
        for (int j = i == 1 ? 1 : a[i-1] + 1; j <= 2e5; j++) {
            int g = i == 2 ? 0 : gcd(a[i-1], a[i-2]);
            if (j == g + a[i-1] && t[j] == 0) {
                a[i] = j;
                t[j] = 1;
                break;
            }
        }
    }

    for (int i = 1; i <= n; i++)
        printf("%d ", a[i]);

    return 0;
}

此外,为了避免重复计算,我们可以将计算过的值存储到一个桶中,以便下次查询时直接查找,而不是再进行计算。