数列(贪心思维题)(很有意思哦)(读者 rp +++++++)
序
这是前段时间做的一道题,蛮有思维含量的,而且对于代码实现能力也有一定要求。
作者也交了好多发😂
所以也来”祸害祸害“大家
题意
给定长度为 n n n 的正整数序列 a a a ,重新排列该数列中的数,使得 ∑ i = 1 n − 1 a i a i + 1 \sum_{i=1}^{n-1} a_ia_{i+1} ∑i=1n−1aiai+1 最大。
解法
考虑要使得 ∑ i = 1 n − 1 a i a i + 1 \sum_{i=1}^{n-1} a_ia_{i+1} ∑i=1n−1aiai+1 最大,一定是把最大的放在中间,其它的依次往旁边放
可是这样无法保证字典序
所以就要引出如下做法:
首先排序(以数值 a i a_i ai 为第一关键字,在原序列中的位置 i d i id_i idi 为第二关键字)
(保留一版原序列 b i b_i bi
然后把数值相同的并为一个块,记录每个块的起始 s t st st 、终止 n d nd nd、长度 l n ln ln
令 a n s ans ans 存放答案
枚举每一个块 p p p,令 i = s t p , j = n d p , k = l n p i=st_p,j=nd_p,k=ln_p i=stp,j=ndp,k=lnp
双指针 x , y x,y x,y ,表示当前答案放到的位置
分情况讨论:
-
k = 1 k=1 k=1 ,
若 b a n s x < b a n s y b_{ans_x}<b_{ans_y} bansx<bansy ,则把当前元素放到 x + 1 x+1 x+1
若 b a n s x > b a n s y b_{ans_x}>b_{ans_y} bansx>bansy ,则放到 y − 1 y-1 y−1
若 b a n s x = b a n s y b_{ans_x}=b_{ans_y} bansx=bansy ,则对比 i d i id_i idi 和并不在当前块的 i d i + 1 id_{i+1} idi+1 ,若 i d i < i d i + 1 id_i<id_{i+1} idi<idi+1 ,放左边,反之放右边(显而易见这确实是使得字典序最小的情况)
-
k = 2 k=2 k=2 ,
把字典序小的放在 x + 1 x+1 x+1,字典序大的放在 y − 1 y-1 y−1
不过因为值相同的元素一定是字典序小的在前大的在后,所以把 i i i 放到 x + 1 x+1 x+1 , j j j 放到 y − 1 y-1 y−1 即可
-
k ≥ 2 k \ge 2 k≥2 ,
首先把 i i i 放在 x + 1 x+1 x+1 , j j j 放在 y − 1 y-1 y−1
那么怎么处理 i + 1 ∼ j − 1 i+1 \sim j-1 i+1∼j−1 的这一坨数呢?
我们找到一个基准 t t t
若下一个块的长度 l n p + 1 ≥ 2 ln_{p+1} \ge 2 lnp+1≥2 ,就是下一个块首个元素的字典序 i d s t p + 1 id_{st_{p+1}} idstp+1 ,反之则为 min ( i d s t p + 1 , i d s t p + 2 ) \min(id_{st_{p+1}},id_{st_{p+2}}) min(idstp+1,idstp+2)
对于 i + 1 ∼ j − 1 i+1 \sim j-1 i+1∼j−1 的每一个数 q q q ,若字典序 i d q < t id_q<t idq<t ,则放在左边,反之放在右边
注意注意,易错点来咯
在往右边放的同时,也要考虑字典序的问题,由于指针 y y y 的移动是从后往前的,那么对于 i d q > t id_q>t idq>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;
}
后记
如果你觉得作者写的还不错对你有帮助那就点个赞吧
收藏+关注更好啦
谢谢🌹