数列(贪心思维题)(很有意思哦)(读者 rp +++++++)
序
这是前段时间做的一道题,蛮有思维含量的,而且对于代码实现能力也有一定要求。
作者也交了好多发😂
所以也来”祸害祸害“大家
题意
给定长度为 nnn 的正整数序列 aaa ,重新排列该数列中的数,使得 ∑i=1n−1aiai+1\sum_{i=1}^{n-1} a_ia_{i+1}∑i=1n−1aiai+1 最大。
解法
考虑要使得 ∑i=1n−1aiai+1\sum_{i=1}^{n-1} a_ia_{i+1}∑i=1n−1aiai+1 最大,一定是把最大的放在中间,其它的依次往旁边放
可是这样无法保证字典序
所以就要引出如下做法:
首先排序(以数值 aia_iai 为第一关键字,在原序列中的位置 idiid_iidi 为第二关键字)
(保留一版原序列 bib_ibi
然后把数值相同的并为一个块,记录每个块的起始 ststst 、终止 ndndnd、长度 lnlnln
令 ansansans 存放答案
枚举每一个块 ppp,令 i=stp,j=ndp,k=lnpi=st_p,j=nd_p,k=ln_pi=stp,j=ndp,k=lnp
双指针 x,yx,yx,y ,表示当前答案放到的位置
分情况讨论:
-
k=1k=1k=1 ,
若bansx<bansyb_{ans_x}<b_{ans_y}bansx<bansy ,则把当前元素放到 x+1x+1x+1
若 bansx>bansyb_{ans_x}>b_{ans_y}bansx>bansy ,则放到 y−1y-1y−1
若 bansx=bansyb_{ans_x}=b_{ans_y}bansx=bansy ,则对比 idiid_iidi 和并不在当前块的 idi+1id_{i+1}idi+1 ,若 idi<idi+1id_i<id_{i+1}idi<idi+1 ,放左边,反之放右边(显而易见这确实是使得字典序最小的情况)
-
k=2k=2k=2 ,
把字典序小的放在 x+1x+1x+1,字典序大的放在 y−1y-1y−1
不过因为值相同的元素一定是字典序小的在前大的在后,所以把 iii 放到 x+1x+1x+1 ,jjj 放到 y−1y-1y−1 即可
-
k≥2k \ge 2k≥2 ,
首先把 iii 放在 x+1x+1x+1 ,jjj 放在 y−1y-1y−1
那么怎么处理 i+1∼j−1i+1 \sim j-1i+1∼j−1 的这一坨数呢?
我们找到一个基准 ttt
若下一个块的长度 lnp+1≥2ln_{p+1} \ge 2lnp+1≥2 ,就是下一个块首个元素的字典序 idstp+1id_{st_{p+1}}idstp+1 ,反之则为 min(idstp+1,idstp+2)\min(id_{st_{p+1}},id_{st_{p+2}})min(idstp+1,idstp+2)
对于 i+1∼j−1i+1 \sim j-1i+1∼j−1 的每一个数 qqq ,若字典序 idq<tid_q<tidq<t ,则放在左边,反之放在右边
注意注意,易错点来咯
在往右边放的同时,也要考虑字典序的问题,由于指针 yyy 的移动是从后往前的,那么对于 idq>tid_q>tidq>t 的点,也要从后往前枚举
代码
#include<cstdio>
#include<iostream>
using namespace std;
const int N=1e5;
struct node
{
int vl,id;
}a[N+10];
int b[N+10],ll[N+10],rr[N+10],ln[N+10],ans[N+10];
bool cmp(node x,node y)
{
return (x.vl<y.vl)||((x.vl==y.vl)&&(x.id<y.id));
}
void swp(node &x,node &y)
{
node t=x;
x=y;
y=t;
}
void qsrt(int l,int r)
{
int i=l,j=r;
node mid=a[(l+r)>>1];
while(i<=j)
{
while(cmp(a[i],mid))
{
i++;
}
while(cmp(mid,a[j]))
{
j--;
}
if(i<=j)
{
swp(a[i],a[j]);
i++;
j--;
}
}
if(i<r)
{
qsrt(i,r);
}
if(j>l)
{
qsrt(l,j);
}
}
void clr(int n)
{
for(int i=0;i<=n+1;i++)
{
a[i].vl=a[i].id=b[i]=ll[i]=rr[i]=ln[i]=ans[i]=0;
}
}
void slv()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i].vl);
a[i].id=i;
b[i]=a[i].vl;
}
qsrt(1,n);
int lst=1,cnt=0;
a[0].id=1e9;
a[n+1].vl=a[n].vl+1;
for(int i=2;i<=n+1;i++)
{
if(a[i].vl!=a[i-1].vl)
{
ll[++cnt]=lst;
rr[cnt]=i-1;
ln[cnt]=rr[cnt]-ll[cnt]+1;
lst=i;
}
}
int x=0,y=n+1;
for(int p=1;p<=cnt;p++)
{
int i=ll[p],j=rr[p],k=ln[p];
switch(k)
{
case 1:
{
if(b[ans[x]]<b[ans[y]])
{
ans[++x]=a[i].id;
}
else
{
if(b[ans[x]]>b[ans[y]])
{
ans[--y]=a[i].id;
}
else
{
if(a[i].id<a[i+1].id)
{
ans[++x]=a[i].id;
}
else
{
ans[--y]=a[i].id;
}
}
}
break;
}
case 2:
{
ans[++x]=a[i].id;
ans[--y]=a[j].id;
break;
}
default:
{
ans[++x]=a[i].id;
ans[--y]=a[j].id;
int t;
if(ln[p+1]>=2)
{
t=a[ll[p+1]].id;
}
else
{
t=min(a[ll[p+1]].id,a[ll[p+2]].id);
}
for(int q=i+1;q<j;q++)
{
if(a[q].id<t)
{
ans[++x]=a[q].id;
}
else
{
break;
}
}
for(int q=j-1;q>i;q--)
{
if(a[q].id>t)
{
ans[--y]=a[q].id;
}
else
{
break;
}
}
}
}
}
for(int i=1;i<=n;i++)
{
printf("%d ",ans[i]);
}
printf("\n");
clr(n);
}
int main()
{
int T;
scanf("%d",&T);
for(int i=1;i<=T;i++)
{
slv();
}
return 0;
}
后记
如果你觉得作者写的还不错对你有帮助那就点个赞吧
收藏+关注更好啦
谢谢🌹