解题思路
分三种情况讨论:
x>0
当x>0时,有一种特殊情况,就是x正好等于最大值,且最小值为0的情况。这个时候无论直接升序还是降序都不能满足条件,这个时候可以拿出一个非0且非x的其他数字放在第一位,然后放最大的数字,剩下的就可以随便放了。
否则不是这种特殊情况的你就可以直接降序排序输出。
x=0
当x=0时问题可以转化成:0不能放第一个,并且任意两个相同数字不能相邻,这其实是一个经典的鸽巢问题,即只要出现次数最多的数字不多于一半即可。
但是实际构造的过程中可以借助一个优先队列,每次都出队当前次数最多的数字即可。
x<0
因为输入的数组全部为非负整数,所以x<0时最简单,直接升序输出即可。
但是实际上你代码完全不用写的这么麻烦,你只用尝试一下 升序、降序、相邻不相同这三种方法是不是有一种可行,如果三种都不可行,说明无解。
代码
#include<iostream>
#include<cstdio>
#include<iomanip>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define ll long long
using namespace std;
int T;
int n,flag,x,w[100010],m,cnt,a[100010],ans[100010];
bool cmp(int x,int y) {
return x>y;
}
int main() {
scanf("%d",&T);
while(T--) {
scanf("%d%d",&n,&x);
m=0,cnt=0,flag=0;
for(int i=1; i<=n; i++) {
scanf("%d",&w[i]);
f[w[i]]=0;
if(w[i]!=x)
a[++m]=w[i];
else cnt++;
}
sort(w+1,w+n+1);
if(x==0) {
ll num=0,now=0,g=0,num0=0;
for(ll i=1;i<=n;++i){
if(w[i]==w[i-1])now++;
else{
if(now>num)num=now,g=w[i-1];
now=1;
}
if(!w[i])num0++;
}
if(now>num)num=now,g=w[n];
if(num0<=n/2&&num<=(n+1)/2){
printf("yes\n");
if(num==(n+1)/2&&g){
for(ll i=1;i<=n;i+=2)
ans[i]=g;
now=1;
for(ll i=2;i<=n;i+=2){
while(w[now]==g)now++;
ans[i]=w[now];
now++;
}
}
else{
now=1;
for(ll i=2;i<=n;i+=2)
ans[i]=w[now],now++;
for(ll i=1;i<=n;i+=2)
ans[i]=w[now],now++;
}
for(ll i=1;i<=n;++i)
printf("%lld ",ans[i]);
printf("\n");
}
else printf("no\n");
continue;
}
if(x<0)
sort(a+1,a+m+1);
else sort(a+1,a+m+1,cmp);
for(int i=1; i<m; i++) {
if((x-a[i]!=x&&a[i+1]-x!=x)) {
flag=i;
break;
}
}
if(x-a[m]!=x)flag=m;
if(!flag) {
printf("no\n");
continue;
} else printf("yes\n");
for(int i=1; i<=m; i++) {
if(i!=flag)
printf("%d ",a[i]);
else {
printf("%d ",a[i]);
for(int j=1; j<=cnt; j++)
printf("%d ",x);
}
}
printf("\n");
}
}
/*
5
10 0
0 0 0 0 0 1 2 3 4 5
11 0
0 0 0 0 0 1 1 1 1 1 1
10 0
0 1 2 3 4 5 6 7 8 9
11 0
0 0 0 1 1 1 1 2 3 4 5
10 0
0 0 0 9 9 9 5 5 8 9
*/