数列(贪心思维题)(很有意思哦)(读者 rp +++++++)

数列(贪心思维题)(很有意思哦)(读者 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=1n1aiai+1 最大。

解法

考虑要使得 ∑ i = 1 n − 1 a i a i + 1 \sum_{i=1}^{n-1} a_ia_{i+1} i=1n1aiai+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 ,表示当前答案放到的位置

分情况讨论:

  1. 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 y1

    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 ,放左边,反之放右边(显而易见这确实是使得字典序最小的情况)

  2. k = 2 k=2 k=2

    把字典序小的放在 x + 1 x+1 x+1,字典序大的放在 y − 1 y-1 y1

    不过因为值相同的元素一定是字典序小的在前大的在后,所以把 i i i 放到 x + 1 x+1 x+1 j j j 放到 y − 1 y-1 y1 即可

  3. k ≥ 2 k \ge 2 k2

    首先把 i i i 放在 x + 1 x+1 x+1 j j j 放在 y − 1 y-1 y1

    那么怎么处理 i + 1 ∼ j − 1 i+1 \sim j-1 i+1j1 的这一坨数呢?

    我们找到一个基准 t t t

    若下一个块的长度 l n p + 1 ≥ 2 ln_{p+1} \ge 2 lnp+12 ,就是下一个块首个元素的字典序 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+1j1 的每一个数 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;
}

后记

如果你觉得作者写的还不错对你有帮助那就点个赞吧

收藏+关注更好啦

谢谢🌹

  • 18
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值