试题 算法提高 逆序对
内存限制:256.0MB C/C++时间限制:1.0s Java时间限制:3.0s Python时间限制:5.0s
问题描述
SORT公司是一个专门提供排序服务的公司,该公司的宗旨是:“顺序是美丽的”。他们的工作是通过一系列移动,将某些物品按顺序摆好。他们的服务是通过工作量来计算的,即移动物品的次数。所以,在工作前必须先考察工作量,以便向客户提出收费数目。
用户并不需要知道精确的移动次数,实质上,大多数人都是凭感觉来认定这一列物品的混乱程度。根据SORT公司的经验,人们一般是根据“逆序对”的数目多少来称呼这一序列的混乱程度。假设将序列中第I件物品的参数定义为Ai,那么排序就是将A1,……An从小到大排序。若i<j且Ai>Aj,则<i,j>就为一个“逆序对".
SORT公司请你写一个程序,在尽量短的时间内统计出”逆序对“的数目。
输入格式
第1行为n(1<=n<=100000)
接下来N行,每行一个长整数型范围内的整数。
输出格式
一个整数,为逆序对的数目。
样例输入
5
3
1
4
5
2
样例输出
4
理解
1.逆序对:设 A 为一个有 n 个数字的有序集 (n>1),其中所有数字各不相同。如果存在正整数 i, j 使得 1 ≤ i < j ≤ n 而且 A[i] > A[j],则 A[i], A[j]> 这个有序对称为 A 的一个逆序对,也称作逆序数。
2.本题目用归并算法,在每层递归中累加逆序对的数量
提交代码
#include<iostream>
using namespace std;
const int N=1e6+10;
int a[N],tmp[N];
int merge_sort(int a[],int l,int r){
if(l>=r)return 0;
int mid=l+r>>1;
int ret=merge_sort(a,l,mid)+merge_sort(a,mid+1,r);
int x=l,y=mid+1,k=0;
while(x<=mid&&y<=r)
if(a[x]<=a[y])tmp[k++]=a[x++];
else {
tmp[k++]=a[y++];ret+=mid+1-x;
}
while(x<=mid)tmp[k++]=a[x++];
while(y<=r)tmp[k++]=a[y++];
for(int i=l,j=0;i<=r;i++,j++)a[i]=tmp[j];
return ret;
}
int main(){
int n;cin>>n;
for(int i=0;i<n;i++)cin>>a[i];
cout<<merge_sort(a,0,n-1);
return 0;
}
但是数据大小有点问题,改成long long ,龙龙🐉这样就过了
#include<iostream>
using namespace std;
typedef long long ll;
const int N=1e6+10;
int a[N],tmp[N];
ll merge_sort(int a[],int l,int r){
if(l>=r)return 0;
int mid=l+r>>1;
ll ret=merge_sort(a,l,mid)+merge_sort(a,mid+1,r);
int x=l,y=mid+1,k=0;
while(x<=mid&&y<=r)
if(a[x]<=a[y])tmp[k++]=a[x++];
else {
tmp[k++]=a[y++];ret+=mid+1-x;
}
while(x<=mid)tmp[k++]=a[x++];
while(y<=r)tmp[k++]=a[y++];
for(int i=l,j=0;i<=r;i++,j++)a[i]=tmp[j];
return ret;
}
int main(){
int n;cin>>n;
for(int i=0;i<n;i++)cin>>a[i];
cout<<merge_sort(a,0,n-1);
return 0;
}
总结:
归并算法来源于acwing,算法模板如下:
#include <iostream>
using namespace std;
typedef long long ll;//long long比较长写起来比较麻烦,故用typedef
ll merge_sort(int l, int r);//该函数的功能为直接返回逆序对个数
const int N = 1e5 + 10;
int q[N], n, a[N];
int main()
{
cin >> n;
for (int i = 0; i<n; i++) cin >> q[i];
cout << merge_sort(0, n - 1) <<endl;
return 0;
}
ll merge_sort(int l, int r)
{
if (l == r) return 0;//递归的终点
int mid = l + r >> 1;
ll count = merge_sort(l, mid) + merge_sort(mid + 1, r);//将左右逆序对数量先加总
int i = l, j = mid + 1, k = 0;
while(i<=mid && j<=r)
{
if (q[i]<=q[j]) a[k++] = q[i++];
else
{
count = count + (mid + 1 - i);//由上图可知此处右侧每个数能与左侧构成的逆序对有mid + 1 -i个
a[k++] = q[j++];
}
}
while(i<=mid) a[k++] = q[i++];//扫尾
while(j<=r) a[k++] = q[j++];//扫尾
for (i = l, k = 0; i<=r;) q[i++] = a[k++];//归还数组元素
return count;//返回逆序对个数
}//该代码引用AcWing网站的代码