至于为何冒泡排序的次数,是逆序对的数量?
这里说一下从小到大冒泡的问题。
对于一个数字k(一开始在数组的k的位置), 在冒泡排序中,只有出现有一个数字在a[k]前面,并且比a[k]要大,这个数字才会交换位置。并且只会向前交换。
显然,对于排序结束后的数列, a[k]前面是不会有比他大的数字了, 同时!a[k]只会和在他前面,比他大的数字交换(冒泡排序中,交换位置的判定。) 那么,交换的次数,就是a[k]在初始序列中,前面有多少个数字比他大。 这也就是逆序对的问题了。
方法1:分治
思考归并排序, 对2个已经排好序的数列,进行再排序,只需要把2个数列,从头到尾,按顺序,谁小,谁就先进入tmp数组, 最后tmp数组一定排好序了,然后把TMP数组的元素复制回原数组中即可。
同理,如果我们知道2个子序列的逆序对数量,是否可以通过归并排序一样,求出整体的数量呢?显然是可以的。
这里有一个地方,当左边的数列的a[k]要进tmp数组了, 这个时候,如果右边的指针指向a+mid+p,就说明a[k]比a[mid+1]...a[mid + 2]..a[mid+3].....a[mid+p]都要大!【重要】
也就是说,对于a[k]而言,整个数列中, mid+ mid+2...mid+p都在k后面,同时a[k]比a[mid+1],a[mid+2]...a[mid+p]都要大。 那么显然是增加逆序对数量的。 通过整个方法,计算出整个逆序对的数量即可。
#include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;
const int max_n = 1000 + 10;
int n, a[max_n];
int tmp[max_n], ans;
void merge(int *a, int *tmp, int l, int mid, int r)
{
if (l >= r) return;
int i = l, j = mid + 1, k = 0;
int count = 0, flag = 0;
while (i <= mid && j <= r)
{
if (a[i] <= a[j])
{
tmp[k ++] = a[i++];
ans += j - mid - 1;
}else tmp[k ++ ] = a[j++];
}
while (i <= mid) tmp[k ++] = a[i++], ans += r- mid;
while (j <= r) tmp[k ++] = a[j++];
for (i = 0; i != k; ++ i) a[l + i] = tmp[i];
}
void mergesort(int *a, int *tmp, int l, int r)
{
if (l >= r) return;
int mid = (l + r) / 2;
mergesort(a, tmp, l, mid);
mergesort(a, tmp , mid + 1, r);
merge(a, tmp, l, mid, r);
}
int main()
{
int tt;
scanf("%d", &tt);
for (int i = 1; i <= tt; ++ i)
{
cout<<"Scenario #"<<i<<":"<<endl;
scanf("%d", &n);
ans = 0;
for (int i = 0; i != n; ++ i) scanf("%d", &a[i]);
mergesort(a, tmp, 0, n - 1);
cout<<ans<<endl<<endl;
}
}
比较快。
1804 | Accepted | 736K | 32MS | G++ | 1050B |