C. Manhattan Permutations

题目要求
  • 排列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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值