lyd讀書筆記 0x05 排序(下)
阿新 • • 發佈:2019-02-03
終於看到了完結的曙光。。話說我規劃今天做後面的題誒。。
k大數
隨機選取一個數,將比它大的放在左邊,小的放在右邊,設有個比它小的,就在左半段找,否則去右半段。這樣遞迴即可,複雜度。
萬能的STL有nth_element()
分治排序與逆序對
#include <iostream>
using namespacestd;
int n, s, a[100005], t[100005], i;
void mergesort(int l, int r)
{
if (l == r)
return;
int mid = (l + r) / 2;
int p = l;
int i = l;
int j = mid + 1;
mergesort(l, mid);
mergesort(mid + 1, r);
while (i <= mid && j <= r)
{
if (a[j] < a[i])
{
s += mid – i + 1;
t[p] = a[j];
p++;
j++;
}
else
{
t[p] = a[i];
p++;
i++;
}
}
while (i <= mid)
{
t[p] = a[i];
p++;
i++;
}
while (j <= r)
{
t[p] = a[j];
p++;
j++;
}
for (i = l; i <= r; i++)
a[i] = t[i];
}
int main()
{
cin >> n;
for (i = 1; i <= n; i++)
cin >> a[i];
mergesort(1, n);
cout << s << endl;
return 0;
}
覺不覺得這個程式碼有點熟悉。。
其實這個程式碼是2017初賽程式填空T3的程式碼。。原封不動的拿過來了。。
原理這裡稍微解釋一下吧,重點是merge的部分。
首先我們考慮一下,如果不進行求逆序對,那麼我們的過程是怎樣的。其實很簡單,我們建兩個指標然後滾一遍,碰到下一個數就比較大小。顯而易見的這個時候我們的兩個子陣列是已經排序完成的,所以直接維護即可。當然如果到最後發現還有一個數組有剩餘,就直接放在最後。
那麼我們考慮哪些地方對逆序對有貢獻。顯而易見的,一個是子陣列內部,一個是兩個陣列之間。但是我們知道,對於一個遞迴過程而言,子陣列內部的一定已經處理完畢了,所以只需要考慮兩個子陣列之間的貢獻,那做法就很顯然了,如果第二個陣列的元素比第一個陣列的某個元素要小,就意味著這個元素比第一個陣列剩餘元素都要小,所以我們加上個元素。
當然順序對也是同理的,我們考慮第一個陣列對第二個陣列的貢獻即可。這樣的複雜度是的。
模板題:POJ2299,記得開LL。。
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
#define N 500005
int a[N], c[N], t;
long long cnt;
void merge(int l, int r) {
if(l == r) return;
int mid = l + r >> 1;
merge(l, mid);
merge(mid + 1, r);
int i = l, j = mid + 1, p = l;
while(i <= mid && j <= r) {
if(a[i] > a[j]) {
cnt += mid - i + 1;
c[p] = a[j];
++j; ++p;
} else {
c[p] = a[i];
++i; ++p;
}
}
if(i <= mid)
for(int k = i; k <= mid; ++k)
c[p++] = a[k];
if(j <= r)
for(int k = j; k <= r; ++k)
c[p++] = a[k];
for(int k = l; k <= r; ++k) a[k] = c[k];
}
int main() {
int n;
while(~scanf("%d", &n)) {
if(n == 0) break;
for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
merge(1, n);
printf("%lld\n", cnt);
cnt = 0;
}
return 0;
}
奇數碼先打lazytag了。。。