关于排序

update 2.16

一个晚自修的想法

对一个序列\(a\)进行排序

如:\(a\)\(2\) \(7\) \(8\) \(1\) \(5\) \(4\)

计算其“上坡”(上升序列),记录每个“上坡”的起始位置

如:\((2\) \(7\) \(8)\) \((1\) \(5)\) \((4)\)

记录起始位置为:

\(up[1]=1,up[2]=4,up[3]=6,up[4]=7\)\(up[4]\)记录\(a\)最后一位的下一位)

同理,记录“下坡”

\(down[1]=1,down[2]=2,down[3]=3,down[4]=5,down[5]=7\)

比较\(up\)\(down\)的长度,\(up\)更短,故以升序排序

对每两个相邻的“上坡”做一次merge操作,变化过程为:

\((2\) \(7\) \(8)\) \((1\) \(5)\) \((4)\)

\((1\) \(2\) \(5\) \(7\) \(8)\) \((4)\)

\((1\) \(2\) \(4\) \(5\) \(7\) \(8)\)

同理,若\(down\)更短,则降序排序后逆序输出(存储)

设“上坡”个数为\(p\),“下坡”个数为\(q\),则时间复杂度为:

\(O[\min(p,q)n],(p,q\le n)\)

P1177实测代码

#include<cstdio>
int n;
int a[200000];
int up[200000];
int down[200000];
int uptot,downtot;
int now;
inline void merge(int l,int mid,int r,int type)
{
	int i=l,j=mid+1,k=l;
	int b[200000];
	while(i<=mid&&j<=r)
	{
		if(type==1)//up
		{
			if(a[i]<a[j]) b[k++]=a[i++];
			else b[k++]=a[j++];
		}
		else//down
		{
			if(a[i]>a[j]) b[k++]=a[i++];
			else b[k++]=a[j++];
		}
	}
	while(i<=mid) b[k++]=a[i++];
	while(j<=r) b[k++]=a[j++];
	for(i=l;i<=r;i++) a[i]=b[i];
}
inline void swap(int &x,int &y)
{
	int tmp=x;
	x=y;
	y=tmp;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	now=a[1];
	up[++uptot]=down[++downtot]=1;
	for(int i=1;i<=n;i++)
	{
		if(a[i]<now) up[++uptot]=i;
		if(a[i]>now) down[++downtot]=i;
		now=a[i];
	}
	up[++uptot]=down[++downtot]=n+1;
	if(uptot<downtot)
		for(int i=1,j=2;j<uptot;j++) merge(up[i],up[j]-1,up[j+1]-1,1);
	else
	{
		for(int i=1,j=2;j<downtot;j++) merge(down[i],down[j]-1,down[j+1]-1,2);
		for(int i=1,j=n;i<j;i++,j--) swap(a[i],a[j]);
	}
	for(int i=1;i<=n;i++) printf("%d ",a[i]);
	return 0;
}

在最坏情况下,应该是\(p=q=\frac{1}{2}n\),退化成\(O(n^2)\)

#2会TLE,求优化方案

(什么LaTeX,不改成代码块都会变成一级标题)

update 2.18(偷偷用教室的电脑打的)

发现之前的搞错了,并不能比较数组长度

hack data:

1 2 3 4 5 6 7 8 7 6 5 4 3 2

显然,\(p=7\),\(q=8\),但:

升序需赋值:\(9+10+11+12+13+14=69\)

降序需赋值:\(2+3+4+5+6+7+14=41\)

故应:

\([\sum_{i=1}^p(p-i+1)P_i]-P_1\le[\sum_{i=1}^q(q-i+1)Q_i]-Q_1\)

则以升序排序

否则以降序排序

其中:\(P_i/Q_i\)为第\(i\)个“上坡”/“下坡”的长度

#include<cstdio>
int n;
int a[200000];
int up[200000];
int down[200000];
int P[200000];
int Q[200000];
int sump,sumq;
int uptot,downtot;
int now;
inline void merge(int l,int mid,int r,int type)
{
	int i=l,j=mid+1,k=l;
	int b[200000];
	while(i<=mid&&j<=r)
	{
		if(type==1)//up
		{
			if(a[i]<a[j]) b[k++]=a[i++];
			else b[k++]=a[j++];
		}
		else//down
		{
			if(a[i]>a[j]) b[k++]=a[i++];
			else b[k++]=a[j++];
		}
	}
	while(i<=mid) b[k++]=a[i++];
	while(j<=r) b[k++]=a[j++];
	for(i=l;i<=r;i++) a[i]=b[i];
}
inline void swap(int &x,int &y)
{
	int tmp=x;
	x=y;
	y=tmp;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	now=a[1];
	up[++uptot]=down[++downtot]=1;
	for(int i=1;i<=n;i++)
	{
		if(a[i]<now) up[++uptot]=i;
		if(a[i]>now) down[++downtot]=i;
		now=a[i];
	}
	up[++uptot]=down[++downtot]=n+1;
   
   for(int i=1;i<uptot;i++)
   {  
   		P[i]=up[i+1]-up[i];
  		 sump+=(uptot-i)*P[i];
  }
   for(int i=1;i<downtot;i++)
   {
   		Q[i]=down[i+1]-down[i];
  		 sumq+=(downtot-i)*Q[i];
   }
	if(sump<=sumq)
		for(int i=1,j=2;j<uptot;j++) merge(up[i],up[j]-1,up[j+1]-1,1);
	else
	{
		for(int i=1,j=2;j<downtot;j++) merge(down[i],down[j]-1,down[j+1]-1,2);
		for(int i=1,j=n;i<j;i++,j--) swap(a[i],a[j]);
	}
	for(int i=1;i<=n;i++) printf("%d ",a[i]);
	return 0;
}

时间复杂度为玄学(?)

然而TLE了更多(?,指#2和#4)

posted @ 2023-08-25 20:56  osfly  阅读(26)  评论(0)    收藏  举报