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

posted @ 2022-08-25 00:32  liangbowen  阅读(16)  评论(0编辑  收藏  举报