求逆序对的题目,可以用暴力、树状数组来实现。
1。暴力法:O(n^2)对于1000的规模完全可以做,直接找第i个元素之后有多少个比其小的元素即可。
代码:
using namespace std;
int main()
{
int t,n,cnt=0;
int a[1005];
int ret;
scanf("%d",&t);
while(t--)
{
cnt++;
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
ret=0;
for(int i=0;i<n;i++)
for(int j=i+1;j<n;j++)
if(a[i]>a[j])
ret++;
printf("Scenario #%d:/n",cnt);
printf("%d/n",ret);
if(t)
printf("/n");
}
return 0;
}
运用树状数组,每次维护小于K的数的个数,从后往前求和,时间复杂度是O(nlogn)的,注意数的范围很大,是2000000,这样用这种方法反而不快。
代码:
using namespace std;
const int MAX=2000100;
int seg[MAX],a[1100];
void add(int k)
{
while(k<MAX)
{
seg[k]++;
k+=k&-k;
}
}
int sum(int k)
{
int ret=0;
while(k)
{
ret+=seg[k];
k-=k&-k;
}
return ret;
}
int main()
{
int t,n,cnt=0;
int ret;
scanf("%d",&t);
while(t--)
{
cnt++;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
a[i]+=1000001;
}
ret=0;
memset(seg,0,sizeof(seg));
for(int i=n-1;i>=0;i--)
{
ret+=sum(a[i]-1);
add(a[i]);
}
printf("Scenario #%d:/n",cnt);
printf("%d/n",ret);
if(t)
printf("/n");
}
return 0;
}
我也尝试写了一下,但是超时了,由于数的范围大,而线段树的初始化时间效率低,线段树的初始化时间是O(nlogn)而树状数组的初始化时间是O(n)的,这里差距很大。
总结:能用树状数组代替线段树的,尽量代替,不仅代码量小,而且效率高。