CF1624C 题解
\(\color{red}{see}\space \color{green}{in}\space \color{blue}{my}\space \color{purple}{blog}\)
小学生又双叒叕来写题解啦!
这题还是很简单的,甚至不需要像其他题解一样在最后扫全排列,按顺序计算即可。
从算法方面来说,就是模拟。
从全排列的定义入手,一个长度为 \(n\) 的全排列满足两点:
-
对于每个数 \(a_i\),满足 \(1 \le a_i \le n\)。
-
所有数互不相同。
我们只需要让每一个数疯狂除以 \(2\),直到有一个空位,就停下。
如果一个数一直除,直到那个数都等于 \(0\) 了,就说明无法构造全排列。
如果运行了 \(n\) 次后仍然没有退出,说明 \(n\) 个空位都被完好无缺地填好了。
肯定有同学会问,为什么能按顺序计算呢,万一后面的数比前面的数使用范围更广怎么办?
如果两个数可以到同样的位置,那么可以到达的更小的位置应该也是相同的。
这就说明,找最大的空位填即可。
代码实现相对较简单。
由于有多组数据,所以每次操作前要先初始化。
//这一步非常重要,很容易忘掉。
memset(use, false, sizeof(use));
输入完后,我们一直判断 \(a_i\) 是否符合范围即可。
while (true)
{
if (a[i] == 0) return false; //无法构造全排列。
if (1 <= a[i] && a[i] <= n && use[a[i]] == false)
{
//符合范围并且没用过,就标记并跳出。
use[a[i]] = true;
break;
}
a[i] /= 2;
}
如果运行到这一步,就说明可以构造全排列。
return true; //返回可以就好了。
完整代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
bool use[55];
int a[55];
bool solve()
{
memset(use, false, sizeof(use)); //这一步非常重要,很容易忘掉。
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++)
{
while (true)
{
if (a[i] == 0) return false; //无法构造全排列。
if (1 <= a[i] && a[i] <= n && use[a[i]] == false)
{
//符合范围并且没用过,就标记并跳出。
use[a[i]] = true;
break;
}
a[i] /= 2;
}
}
return true;
}
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
if (solve()) puts("YES");
else puts("NO");
}
return 0;
}
首发:2022-04-14 16:56:20