题目要求
排列p的曼哈顿值是|p_1 - 1| + |p_2 - 2| + ... + |p_n - n|
要求你确定是否存在长度为n的排列p,使得其曼哈顿值等于k,如果存在请你输出构造的排列
思路
对于每个排列来讲,其曼哈顿值一定是偶数
并且题目要求的曼哈顿值k一定不能超过我们排列的限定范围
这个限定范围最小是0,就是我们正序排,最大是就是我们倒叙排
那么我们可以看到,其实交换两个位置,我们得到的贡献就是2倍的这两个位置交换后,下标与下标的差值。
我们可以使用双指针,先把排列按照正序排列,贪心的去尽量的接近我们题目要求的曼哈顿值,如此得到答案。
左指针L,右指针R,正序排列的话交换L,R对曼哈顿值增加2*(R - L)
k>=2*(R-L),交换L,R,L ++,R--
k<2*(R - L),R --
代码
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define endl "\n"
#define int long long
#define debug(x) cout << #x << " " << x << endl
void solve()
{
int n, k;
cin >> n >> k;
vector<int> p(n + 1);
for (int i = 1; i <= n; i++) {
p[i] = i;
}
// 求上下边界
int minn = 0;
int maxn = 0;
for (int i = 1; i <= n; i++) {
int x = (n - i + 1);
maxn = maxn + abs(x - i);
}
// 非法条件
if (k % 2) {
cout << "NO" << endl;
return;
}
if (k < minn || k > maxn) {
cout << "NO" << endl;
return;
}
// 双指针处理
int L = 1, R = n;
while (L <= R)
{
if (k >= 2 * (R - L)) {
swap(p[L], p[R]);
k -= 2 * (R - L);
L++;
R--;
}
else if (k < 2 * (R - L)) {
R--;
}
}
cout << "YES" << endl;
for (int i = 1; i <= n; i++) {
cout << p[i] << " ";
}
cout << endl;
}
signed main()
{
int T;
cin >> T;
while (T--)
{
solve();
}
return 0;
}