1806. 还原排列的最少操作步数
LeetCode: 1806. 还原排列的最少操作步数
中等 \color{#FFB800}{中等} 中等
给你一个偶数
n
,已知存在一个长度为n
的排列perm
,其中perm[i] == i
(下标 从 0 开始 计数)。一步操作中,你将创建一个新数组
arr
,对于每个i
:如果
i % 2 == 0
,那么arr[i] = perm[i / 2]
如果i % 2 == 1
,那么arr[i] = perm[n / 2 + (i - 1) / 2]
然后将
arr
赋值给perm
。要想使
perm
回到排列初始值,至少需要执行多少步操作?返回最小的 非零 操作步数。
示例 1:
输入:n = 2
输出:1
解释:最初,perm = [0,1]
第 1 步操作后,perm = [0,1]
所以,仅需执行 1 步操作
示例 2:
输入:n = 4
输出:2
解释:最初,perm = [0,1,2,3]
第 1 步操作后,perm = [0,2,1,3]
第 2 步操作后,perm = [0,1,2,3]
所以,仅需执行 2 步操作
示例 3:
输入:n = 6
输出:4
提示:
2 <= n <= 1000
n
是一个偶数
方法1:模拟
首先我们要计算对于某个数字 i
,它经过题中的运算后,需要几次运算会变回自身。对于一整个数组 [0, 1, ..., n-1]
,我们需要计算每一个数字的 运算次数 ,然后取这些次数的 最小公倍数 ,即为答案。
我们不需要数组去存储这些最小公倍数。对于某个数字 i
,我们计算出从 1
到 i-1
这些数字中各自 运算次数 的最小公倍数,然后再与 i
的 运算次数 再求最小公倍数即可。
#include <numeric>
class Solution
{
public:
int reinitializePermutation(const int n)
{
int ans = 1;
for (int i = 1; i <= n - 1; i++)
{
int temp = i;
int count = 0;
while (true)
{
temp = ((temp & 0x01) == 0) ? (temp >> 1) : ((n + temp - 1) >> 1);
count++;
if (temp == i)
break;
}
ans = std::lcm(ans, count);
}
return ans;
}
};
复杂度分析:
-
时间复杂度: O ( n 2 ) O(n^2) O(n2)。对于 i ∈ [ 0 , n − 1 ] i \in [0, n-1] i∈[0,n−1] ,i 最多需要变换
n
次。因此时间复杂度为 O ( n 2 ) O(n^2) O(n2)。 -
空间复杂度: O ( 1 ) O(1) O(1)。所用变量的占用空间与输入无关,仅占用常数空间。
参考结果
Accepted
66/66 cases passed (8 ms)
Your runtime beats 45.38 % of cpp submissions
Your memory usage beats 46.22 % of cpp submissions (5.9 MB)