一道编程题:求逆序对的个数
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一道编程题:求逆序对的个数相关的知识,希望对你有一定的参考价值。
给定一个序列a1,a2,…,an,如果存在i<j并且ai>aj,那么我们称之为逆序对,求逆序对的数目
输入:第一行为n,表示序列长度,接下来的n行,第i+1行表示序列中的第i个数。
输出:两行,第一行为所有逆序对总数,第二行为本质不同的逆序对总数。
样例输入:
4
3
2
3
2
样例输出:
3
1
数据范围:N<=105。Ai<=105。时间限制为1s。
哪位大牛帮下忙,讲下怎么用归并排序或树状数组做,主要是第二问,麻烦讲详细的,有程序的最好,PASCAL和C++的都可以
#define N 105
void main()
int n,i,j,k=0,p,m=0;
int a[20];
scanf("%d",&n);
getchar();
for(i=0;i<n;i++)
scanf("%d",&a[i]);
getchar();
for(i=0;i<n;i++)
for(j=i+1;j<n;j++)
if(a[i]>a[j])
k++;
for(i=0;i<n;i++)/*去掉相同的元素,再进行排序*/
for(j=i+1;j<n;j++)
if(a[i]=a[j])
for(p=j+1;p<n;p++)
a[p-1]=a[p];
n--;
for(i=0;i<p;i++)
for(j=i+1;j<p;j++)
if(a[i]>a[j])
m++;
printf("%d\n%d",k,m);
//呵呵,我只会用c写,不过结果是对的。 参考技术A 6分之一,把26个数看成4个数来计算,
得出的答案是一样的,
就是六分之一 参考技术B 前面改成int main(),如果测试程序有时间限制的话,这个算法不够优化,时间超出限制,不过改之后是可以运行的
怎么求逆序对的数量呢?一种特殊写法告诉你
求逆序对的必备知识
我们要求逆序对,就要求前面的数比后面的数大的数对有多少个。
那么如果我们把每个数前面比它大的数有多少个求出来就可以了,答案就显而易见,就是每个数前面比它大的数的个数的和。
那么问题转化为求每个数前面比它大的数的个数。
怎么求呢?
我们考虑到可以用树状数组tr[]维护一个数组
先将原数组进行离散化,因为可能原数组的数据范围很大,数组空间不能开的很大。离散化之后原数组就变成了一个相对大小等级的数。(数小代表这个数值小相对其他数小)
然后每访问一个数,就把树状数组相对应等级的位置加一,代表该等级有一个数存在,然后i-sum(a[i])代表已经访问数的个数减去小于等于a[i]的数的个数(其实就是前面出现的大于a[i]的数的个数)
注意:树状数组存储的是数的大小等级(相对大小)
累加一下就好了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e5+5;
ll a[N],b[N],tr[N];
ll n;
void add(int p,int x)
{
while(p<=n){tr[p]+=x;p+=p&-p;}
}
ll sum(int x)
{
ll res = 0;
while(x){res+=tr[x];x-=x&-x;}
return res;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++) {cin>>a[i];b[i]=a[i];}
sort(b+1,b+1+n);//必须先排序才能去重
int len = unique(b+1,b+1+n)-b-1;//去重
for(int i=1;i<=n;i++)
a[i] = lower_bound(b+1,b+1+len,a[i])-b;
ll ans = 0;
for(int i=1;i<=n;i++)
{
add(a[i],1);
ans += i-sum(a[i]);
// cout<<i-sum(a[i])<< "\\n";
}
cout<<ans<<'\\n';
return 0;
}
以上是关于一道编程题:求逆序对的个数的主要内容,如果未能解决你的问题,请参考以下文章