★实验任务
给定一个1~N的排列P,即1到N中的每个数在P都只出现一次。 现在要对排列P进行冒泡排序,代码如下:
for (int i = 1; i <= N; ++i)
for (int j = N, t; j > i; ‐‐j)
if (P[j ‐ 1] > P[j])
t = P[j], P[j] = P[j ‐ 1], P[j ‐ 1] = t;
在排序过程中,数字的位置可能会发生变化。对于1~ N的每个数字,你需要输出过程中达到的最左位置下标和最右位置下标的差的绝对值。
★数据输入
第一行为N,表示排列长度。 第二行为排列P。 数据保证: 80%的数据,N <= 1000 100%的数据,N <= 100000
★数据输出
输出一行,第i个数字表示数字i最左与最右位置的差的绝对值。
★输入样例
4
3 2 1 4
★输出样例
2 1 2 0
★Hint
样例冒泡排序过程:
swap 2 1: 3 2 1 4 > 3 1 2 4
swap 3 1: 3 1 2 4 > 1 3 2 4
swap 3 2: 1 3 2 4 > 1 2 3 4
解题思路:两种做法:
①直接依题意模拟和更新状态
【TLE WARNING】 只能拿80分…因为是最简单的写法,AC做法是第二个
卡了无数天了没能用会线段树&树状数组
//Matsuri
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAX 200001
int N,P[MAX],l[MAX],r[MAX];
//l[i]返回i最左位置下标,r[i]返回i的最右位置下标
int main()
{
scanf("%d",&N);
for (int i=1;i<=N;i++)
{
scanf("%d",&P[i]);
l[P[i]]=i; //将每个数据的"最左"和"最右"位置信息初始化
r[P[i]]=i;
}
for (int i=1;i<=N;i++)
{
for (int j=N;j>i;j--)
{
if (P[j-1]>P[j]) std::swap(P[j-1],P[j]); //每趟都把小的往前面丢
}
for (int j=1;j<=N;j++)
{
if (j<l[P[j]]) l[P[j]]=j;
if (j>r[P[j]]) r[P[j]]=j;
}
}
for (int i=1;i<=N;i++) printf("%d ",abs(l[P[i]]-r[P[i]]));
return 0;
}
②归并排序优化【可AC】
经过多次尝试后发现,一个数在升序排序过程中能移动到多左边和多右边,取决于该数的右边有多少数据是小于它的。发现这一点之后,我们就能明白整个有序序列是由冒泡排序还是由其他排序方法得到的就无所谓了,观察数据规模,我们得用O(nlogn)或更优做法来解决本题,这里使用了归并排序。
pos[a]为数据a初始所在位置,cnt[a]为位于数据a右边,且小于数据a的数据的数量
a的最左位置下标:min(排序后a所在位置,排序前a所在位置)
a的最右位置下标:pos[a]+cnt[a]
该做法要求对归并排序过程熟悉
//Matsuri
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAX 200001
int N,P[MAX];
int pos[MAX],cnt[MAX];
int min(int a,int b)
{
return a<b?a:b;
}
void Merge(int a[],int L,int mid,int R)
{
int len=R-L+1;
int *tmp=new int[len],c=0;
int i=L,j=mid+1;
while(i<=mid && j<=R)
{
if (a[i]<=a[j]) tmp[c++]=a[j++]; //降序排序
else
{
cnt[a[i]]+=R-j+1; //写成降序排序就是为了这一步,方便统计比a[i]小且位于a[i]后面的数据的数量
tmp[c++]=a[i++];
}
}
while (i<=mid) tmp[c++]=a[i++];
while (j<=R) tmp[c++]=a[j++];
for (int k=0;k<len;k++)
{
a[L++]=tmp[k];
}
}
void MergeSort(int a[],int L,int R)
{
if (L==R) return;
int mid=(L+R)>>1;
MergeSort(a,L,mid);
MergeSort(a,mid+1,R);
Merge(a,L,mid,R);
}
int main()
{
scanf("%d",&N);
for (int i=1;i<=N;i++)
{
scanf("%d",&P[i]);
pos[P[i]]=i;
}
MergeSort(P,1,N);
for (int i=1;i<=N;i++) printf("%d ",pos[i]+cnt[i]-min(i,pos[i]));
return 0;
}
树状数组做法有缘再补上