题目链接:
题意:
给你一个长度为n的数组a,已知他们的和为0,问你是否存在一种重新排列的方案(可以不排列),使得 子数组元素之和的极差 < 最大元素 - 最小元素 的值,若存在输出Yes,以及重新排列后的结果,否则输出No
思路:
首先,我们可以发现若全为0,则一定不成立,直接输出No
否则,一定有解,那么如何去构造呢?
先将数组从小到大排序,定义双指针l和r以及ans数组,以及用来代表前缀和的now
若此时now小于0,则将a[r]加入ans数组(若还没排完,则a[r]一定大于0,因为所有元素之和为0),r--
若此时now大于等于0,则将a[l]加入ans数组(若还没排完,则a[l]一定小于等于0,因为所有元素之和为0),l++
如何证明这种构造方式是正确的?
我们知道,子数组元素之和的极差本质上就是两个前缀和之差,意味着我们要让
MaxPreSum - MinPreSum < 最大元素 - 最小元素
而本文章所使用的构造方式一定可以保证MaxPreSum < 最大元素,MinPreSum <= 最小元素,因此这种构造方式一定是正确的
代码:
#include<bits/stdc++.h>
using LL = long long;
void solve(){
// I know you are here
// Thank you for your appreciation
int n;
std::cin >> n;
std::vector<int> a(n + 1);
bool ok = false;
for(int i = 1; i <= n; i++) {
std::cin >> a[i];
if(a[i] != 0) ok = true;
}
if(!ok) {
std::cout << "No\n";
return;
}
std::sort(a.begin() + 1, a.end());
int l = 1, r = n;
std::vector<int> ans;
int now = 0;
while(l <= r) {
if(now < 0) {
ans.push_back(a[r]);
now += a[r];
r--;
}
else {
ans.push_back(a[l]);
now += a[l];
l++;
}
}
std::cout << "Yes\n";
for(auto it: ans) {
std::cout << it << " ";
}
std::cout << "\n";
}
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(0);std::cout.tie(0);
int _;
std::cin >> _;
while(_--){
solve();
}
return 0;
}