休息【归并排序】【模拟】
题目大意:
给出一个数列,每次可以把一个单调递减的序列反过来,求最少要反多少次才能使这个序列单调递增。
6
5 3 2 1 6 4
3
思路:
对于一个数列,我们可以先按照题目所说的将单调递减的数列取反。
- 5 3 2 1 6 4
就变成
- 1 2 3 5 4 6
这时候,每个曾经单调递减的数列就单调递增了,每个区间内的数列肯定满足单调递增,所以剩下的肯定是两个区间之间的数字不满足单调递增。
那么很容易发现,剩余需要取反的数字个数一定是现在数列逆序对的个数,所以,再用归并排序求出逆序对的个数即可。
代码:
#include <cstdio>
#include <iostream>
using namespace std;
long long n,a[100011],b[100011],sum,i,j;
void make(long long l,long long mid,long long r) //合并
{
long long p1=l,p2=mid+1;
for (long long i=l;i<=r;i++)
{
if (p1>mid) {b[i]=a[p2]; p2++;}
else if (p2>r) {b[i]=a[p1]; p1++;}
else if (a[p1]>a[p2]){sum+=1+r-p2; b[i]=a[p1]; p1++;}
else {b[i]=a[p2]; p2++;}
}
for (long long i=l;i<=r;i++) //重新赋值
a[i]=b[i];
}
void sorts(long long l,long long r) //拆分
{
if (l<r)
{
long long mid=(l+r)/2;
sorts(l,mid);
sorts(mid+1,r); //拆成两半
make(l,mid,r);
}
}
int main()
{
scanf("%lld",&n);
for (i=1;i<=n;i++)
scanf("%lld",&a[i]);
i=1;
j=1;
while (i<n) //先进行一次
{
while (a[j]>a[j+1]&&j<n) j++;
if (j-i)
{
for (long long k=i;k<=i+(j-i+1)/2-1;k++)
swap(a[k],a[j-k+i]);
sum++;
}
i=j=j+1;
}
sorts(1,n); //求逆序对
printf("%lld\n",sum);
return 0;
}