题解——17. 小M的数组变换 | 豆包MarsCode AI刷题

175 阅读3分钟

原题截图

image.png

题意分析

分析题干可以发现,选择两个元素aia_iaja_j以及aia_i的一个因子xx,进行ai:=ai/xa_i:=a_i/xaj:=aj×xa_j:=a_j\times x的操作,实际上就相当于在aia_i的素因子的可重集合SaiS_{a_i}中删去一个元素xx,并将xx加入aja_j的素因子的可重集合SajS_{a_j}中,所以可以将题意转化为以下的等价版本。

有一个数组aa以及nn个可重集合Sa1,Sa2,...,SanS_{a_1},S_{a_2},...,S_{a_n},可以进行的操作为:选择两个可重集合SaiS_{a_i}SajS_{a_j},从SaiS_{a_i}中删去一个元素xx,并将xx加入SajS_{a_j}中(其中对于1in1\leq i\leq n,可重集合SaiS_{a_i}中存放的是aia_i的所有素因子)。问能否通过若干次操作,使每个可重集合中的元素的种类数至多为11,若能,返回"Yes",否则返回"No"

*种类数的解释:例如,可重集合[2,2,2,3,3][2,2,2,3,3]的元素种类数为22,因为所有的22视为同一种元素,所有的33也视为同一种元素。

解题思路

在将题意转化为上述等价版本后,题目的难点仅剩素因子的分解,因为通过素因子的分解得出数组aa中每个数的素因子后,也就得出了可重集合Sai(1in)S_{a_i}(1\leq i\leq n)中的元素。容易得出,题目返回"Yes"的充要条件即为:所有可重集合中的元素的种类数n\leq n,即数组aa中所有数的素因子的种类数n\leq n

对于素因子(后面统一称为“质因数”)的分解,由于豆包MarsCode AI刷题平台的题目未给出数据范围,因此先假设数据范围足够小,直接用试除法分解质因数。具体过程为:设要分解质因数为ai,设ceil(sqrt(ai))sai,从2开始按步长1向后遍历,设遍历到的元素为j,并在不满足j<=ai && j<=sai时结束遍历,如果ai%j == 0ai /= j;,并向记录所有数的素因子的种类数的集合st中放入j,如果遍历完成后,仍然有ai != 1,则向集合st中放入ai。更加准确的过程描述请见代码实现部分。

如果实际的数据范围较大,可以先用埃氏筛、欧拉筛等质数筛在O(n)O(n)或接近O(n)O(n)的时间复杂度下筛选出[1,n][1,n]区间内的质数,然后用筛出来的质数去分解质因数,可以避免遍历到不是质数的数,从而降低一定的时间复杂度。

代码实现

#include <bits/stdc++.h>
using namespace std;
string solution(int n,vector<int>& a)
{
    set<int> st;
    for (auto&& ai : a)
    {
        int sai = ceil(sqrt(ai));
        for(int j=2;j<=ai && j<=sai;j++)
        {
            while(ai%j == 0)
            {
                ai /= j;
                st.insert(j);
            }
        }
        if(ai != 1) st.insert(ai);
    }
    return st.size()<=a.size() ? "Yes" : "No";
}

总结

本题使用简单的试除法分解质因数,设数组aa的长度为nn,数组中元素的最大值为kk,则时间复杂度为O(nk)O(n\sqrt k),空间复杂度为O(nlog2k)O(n\log_2k);如果kk的值较大,可以使用质数筛来降低时间复杂度的常数值(当k\sqrt k来到大约10510^510710^7的数量级时,大约可将复杂度常数值降到原来的110\frac{1}{10}115\frac{1}{15},即这一范围内质数与所有数的个数之比)。如果你有复杂度更优的解法,欢迎在下方评论。