关于排序
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)

浙公网安备 33010602011771号