目录
题目:
题目描述:
给你一个 n ,你需要找到一种 0 到 n-1 的全排列方式排到数组的第 0 到 n-1 位。使得数组的任意一位的 下标 加上 下标位置存放的数 的 和 都是 完全平方数 。
( 下标 + 下标位置的数 == “一个完全平方数” )
思路:
观察样例,一些答案后面的输出似乎有倒序的身影,所以从这里入手。
为什么倒序?我们可以找一个特例来理解(当 n == 5 时)
所以 n == 5 时,我们需要填写的是 0 到 4 ,而 4 是一个完全平方数,我们只用按照4 3 2 1 0的方式排列,序列跟下标互补之后就都会变成 4 ,此时满足题目要求。
那一般的序列怎么解决?
我们可以尝试局部倒序。其实就是样例 3 的情况。
可以推断出操作过程是这样的:
通过要填写的最大数(设为x),找到大于等于这个数的完全平方数(设为upper),然后我们就可以把 x 放到(upper - x)下标处,这样保证了x放到这个位置的时候,与下标的和是upper,而upper就是完全平方数。
同时,随着下标增加,我们放置的数减少,依然能保证他们的和都是upper,直到将(upper - x)这个数放到x位置停止。
然后我们再将 x 更新为(upper - x - 1),开始新一轮的放置,直到 x < 0
真是妙啊。
然后我们需要考虑两个问题,真的是所有的 n 都可以找到这样的序列吗?每次将 x 更新之后还能把 x 放到(upper - x)的位置吗?会不会(upper - x)不在 0 到 x 的范围内?
其实不难证明,假设我们现在要填入的最大的数是 x ,如果 x 是 0 ,那么不难证明,序列 0 就是答案。如果x不是 0 ,那么x必定在两个完全平方数之间(左开右闭)
不妨设小于 x 的完全平方数是 ,那么大于等于 x 的完全平方数自然是
所以:
当 时,upper - x 有最小值 0
当 时,upper - x 有最大值为 2a
但 在 a 取任何值的情况下都成立
所以得出结论:(upper - x)一定会在 0 到 x 的范围内!
我们终于可以放心的写代码了!
思路有了,具体操作请看AC代码
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
int a[N];
int main()
{
std::ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while (t--)
{
int n;
cin >> n;
n--;//虽然给你n,但是我们只用填到n-1
int tt = n;//代表着要填入的数,
int x = n;//填完之后还剩的最大的数
while (x >= 0)
{
int temp = sqrt(x);
if (temp * temp == x)
{
for (int i = 0; i <= x; i++)
a[i] = tt--;
}
else
{
int upper = (temp + 1) * (temp + 1);
for (int i = upper - x; i <= x; i++)
a[i] = tt--;
}
x = tt;
}
for (int i = 0; i <= n; i++)
cout << a[i] << " ";
cout << '\n';
}
return 0;
}