题目大意
有个数列a[i],让其重新排列使得a[i]*a[i+1] (i=1~n-1)求和最大,之后输出重新排列后的编号,要求字典序最小。
正解
这个题是真的烦人,代码也很繁琐,就粗略看看吧。然后具体是贪心做法,假如没有重复的,那么显然是头放最小,尾放次小,头再放此次小,这样。(注意考虑字典序,也就是考虑是头放最小还是次小)之后假如有重复的,就自行讨论一下。
- 如果这个数出现的次数>=2,那么这个数必定会放入待解决数列头和待解决数列尾。
而取决于放多少个进待解决数列头,取决于,他后面的前两个数的最前位置及出现
次数。 - 如果这个数出现的次数=1
a) 如果 i 位置元素的值小于 j 位置元素的值,显然会放到 i 的右方。
b) 大于的话,显然会放到 i 的左方。
c) 等于的话
i. 如果后一个数的最小位置小于现在的数的位置,则放在 j 的左方。
ii. 反之,放在 i 的右方。
讨论的也许与读者的思路有所不同,希望读者自己仔细推一遍。
然后扯一下是先要排序啊,按权值再按编号。
(照抄题解,是真的烦人啊这道题)
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define N 100007
using namespace std;
int t,n,b[N],ans[N];
struct node{
int x,w;
}a[N];
bool cmp(node a,node b){
if(a.x==b.x) return a.w<b.w;
return a.x<b.x;
}
int find(int x){
while(x<n&&a[x+1].x==a[x].x) x++;
return x;
}
int main(){
freopen("permutation.in","r",stdin);
freopen("permutation.out","w",stdout);
scanf("%d",&t);
while(t--){
memset(a,0,sizeof(a));
memset(ans,0,sizeof(ans));
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i].x),a[i].w=i,b[i]=a[i].x;
sort(a+1,a+n+1,cmp);//排序
int l=1,r=1,l1,r1,ll=1,rr=n;//l,r表示我们现在这个数的头和尾,l1,r1表示这个数下一个数的头和尾,包括下面的l2,r2指再下一个数的头和尾
r=find(r);
l1=r1=r+1;
r1=find(r1);//find就是找目前这个数的尾
while(ll<=rr){
if(l1==n+1){
for(int i=l;i<=r;i++)
ans[ll++]=a[i].w;
continue;
}
if(l==r){//若只有一个当前的数只有一个
if(l1==r1){//若它后面的数也只有一个
if(b[ans[ll-1]]<b[ans[rr+1]])
ans[ll++]=a[l].w,ans[rr--]=a[l1].w;
else if(b[ans[ll-1]]>b[ans[rr+1]])
ans[ll++]=a[l1].w,ans[rr--]=a[l].w;
else{
if(a[l].w<a[l1].w)
ans[ll++]=a[l].w,ans[rr--]=a[l1].w;
else ans[ll++]=a[l1].w,ans[rr--]=a[l].w;
}
l=r=r1+1;
r=find(r);
l1=r1=r+1;
r1=find(r1);//当这种情况我们一次可以吧两个数都做完,然后更新l,r,l1,r1
}else{//后面的数不止一个
if(b[ans[ll-1]]<b[ans[rr+1]])
ans[ll++]=a[l].w;
else if(b[ans[ll-1]]>b[ans[rr+1]])
ans[rr--]=a[l].w;
else{
if(a[l].w<a[l1].w)
ans[ll++]=a[l].w;
else ans[rr--]=a[l].w;
}
l=l1,r=r1;
l1=r1=r+1;
r1=find(r1);//这种情况我们无法将两种情况做完,所以l=l1,r=r1
}
}else{//目前的数不止一个
ans[ll++]=a[l++].w;
ans[rr--]=a[r--].w;//前面的两个数是必放前面与后面的,接下来考虑中间的部分前面放几个,后面放几个(这就是这道题的恶心的东西,判断字典序是真的毒瘤)
int l2=r1+1,r2=l2;//要考虑它后两个数
r2=find(r2);
if(l2!=n+1&&a[l1].w>a[l2].w&&(l1==r1)){//若第后两个数比第后一个数优。
int w=r+1;
for(int i=l;i<=r;i++)
if(a[i].w<a[l2].w)
ans[ll++]=a[i].w;
else{
w=i;
break;
}
for(int i=r;i>=w;i--)
ans[rr--]=a[i].w;
}else{//否则
int w=r+1;
for(int i=l;i<=r;i++)
if(a[i].w<a[l1].w)
ans[ll++]=a[i].w;
else{
w=i;
break;
}
for(int i=r;i>=w;i--)
ans[rr--]=a[i].w;
}
l=l1;
r=r1;
l1=r1=r+1;
r1=find(r1);
}
}
for(int i=1;i<=n;i++)
printf("%d ",ans[i]);
printf("\n");
}
}