在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。
如2 4 3 1中,2 1,4 3,4 1,3 1是逆序,逆序数是4。给出一个整数序列,求该序列的逆序数。
Input
第1行:N,N为序列的长度(n <= 50000)
第2 - N + 1行:序列中的元素(0 <= Aii <= 10^9)
Output
输出逆序数
Sample Input
4
2
4
3
1
Sample Output
4
分治的典型题目,可怜的小白以为这题十分简单,毫不犹豫就贴上了自己的代码:
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
#define ll long long
ll a,b,c,d;
ll r[50010];
int main(){
memset(r,0,sizeof(r));
scanf("%lld",&a);
for(b=0;b<a;b++){
scanf("%lld",&r[b]);
}
c=0;
for(ll i=0;i<a;i++){
for(ll j=i+1;j<a;j++){
if(r[i]>r[j]){
c++;
}
}
}
printf("%lld\n",c);
return 0;
}
结果可想而知TLE啦,这道题实在是坑,时间很少,但是数据达到50000,做题不要慌啊。。题解如下:
采用分治递归的方法,首先既然采用递归就要有一个递归结束的点,如果以中间数为基数mid,那么当基数与两侧的数相同时也就是递归结束的点,然后再回溯。但是如果仅仅是回溯的话,不要慌,请看代码:
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
#define ll long long
ll a,b,c,d,x,y;
ll r[50010];
ll solve(ll i,ll j){
if(i==j){
return 0;
}
ll num=0;
ll mid=(i+j)/2;
num+=solve(i,mid);
num+=solve(mid+1,j);
for(x=i,y=mid+1;x<=mid;){
if(y>j){
y=mid+1;
x++;
if(x>mid){
return num;
}
}
if(r[x]>r[y]){
num++;
}
y++;
}
}
int main(){
memset(r,0,sizeof(r));
scanf("%lld",&a);
for(b=0;b<a;b++){
scanf("%lld",&r[b]);
}
c=solve(0,a-1);
printf("%lld\n",c);
}
结果还是TLE,我就纳闷了,难道还有比这更快的算法吗?经过大佬的指点,我算是彻底醒悟了,我们可以定义一个数组,来对已经比较过得区间排序,这个排序的意思是已经比较过得它不影响接下来的比较的,相反还会减小函数的复杂度。不好想呀,小白的我也是看了好久的啊,行了话不多说,上代码:
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
#define ll long long
ll a,b,c,d,x,y,z,m;
ll r[500100],r1[500100];//r记录总的数据,r1用来保存比较过的数组
ll solve(ll i,ll j){
if(i==j){
return 0;//递归结束的点,回溯的起点
}
ll num=0;
ll mid=(i+j)/2;//把区间分割成一个一个的子段
num+=solve(i,mid);
num+=solve(mid+1,j);//对每个子段分治递归
//用r1来记录此区间内的排序,然后对应于r的子段进行重新赋值
for(x=i,y=mid+1,z=0;x<=mid||y<=j;z++){//彻底明白每一个变量的含义
if(y>j){
r1[z]=r[x];
x++;
}
else if(x>mid){
r1[z]=r[y];
y++;
}
else if(r[x]<=r[y]){
r1[z]=r[x];
x++;
}
else{
r1[z]=r[y];
y++;
num+=mid-x+1;
}
}
for(m=0;m<=j-i;m++){
r[i+m]=r1[m];
}
return num;
}
int main(){
scanf("%lld",&a);
memset(r,0,sizeof(r));//看心情清空
for(b=0;b<a;b++){
scanf("%lld",&r[b]);
}
c=solve(0,a-1);
printf("%lld\n",c);
return 0;
}
加油!