Build Permutation

传送门

题意:
给出一个n, 一个序列0 - n - 1, 问构造一个序列,使得构造的序列是由0 - n - 1组成,且对应位置上的数字和对应的下标相加是完全平方数,如果不存在则输出-1,存在则输出这个序列


定理:
一个数n, 那么在\([n, 2n]\)之间至少存在一个完全平方数,证明略

思路:
首先,有一个最简单情况,如果n - 1是完全平方数,那么答案就是所有的到过来,然后思考普遍的情况, n - 1匹配成完全平方数,这个完全平方数只能是 >= 他的,这样的完全平方数有很多,所以仔细一想,有一个贪心的思想在里面,我要取只要取那个最小的即可,因为最小的那个可以覆盖的范围是最大的,如果不取最小的到时候往前推移的时候还是会在以同样的思想进行推移,一步直接取最小的好处就是一次性处理了中间的可以覆盖的区间,而且不会对前面的情况产生影响,代码就出来了


总结:
首先学到了一个定理,其次,这道构造题是从最简单的情况推移过来的,然后想到的思想,实现用递归(满足同样的需求),有很多值可取的情况想想贪心最优解

点击查看代码
#include <bits/stdc++.h>
#define endl '\n'
#define IOS ios::sync_with_stdio(false);
using namespace std;

typedef long long ll;
const ll MAXN = 1e5 + 10;
ll T, n;
bool flag = true;
map<ll, ll> sqr;
vector<ll> ans;

inline void Init()
{
    for (int i = 0; i <= 1000; ++i)
    {
        sqr[i * i] = 1;
    }
}

void get_permutation(ll k)
{
    if (k < 0)
        return;
    auto mnsqr = sqr.lower_bound(k);    //寻找比k大的最小的那个完全平方数
    ll num = mnsqr->first;
    if (num - k < 0)
    {
        flag = false;
        return ;
    }
    for (int i = num - k; i <= k; ++i)
    {
        ans.push_back(i);
    }
    get_permutation(num - k - 1);
}

int main()
{
	IOS; cin.tie(0), cout.tie(0);
    Init();
    cin >> T;
    while (T--)
    {
        flag = true;
        ans.clear();
        cin >> n;
        get_permutation(n - 1);
        if (flag)
        {
            for (int i = ans.size() - 1; i >= 0; --i)
            {
                cout << ans[i] << " ";
            }
            cout << endl;
        }
        else
        {
            cout << "-1" << endl;
        }
    }
	return 0;
}


posted @ 2022-09-02 16:43  YUGUOTIANQING  阅读(59)  评论(0)    收藏  举报