归并排序求逆序对数 (附另两种姿势BIT 线段树)

来源:互联网 发布:cygwin 运行linux程序 编辑:程序博客网 时间:2024/06/11 08:31

求逆序数 三种方法 归并排序 树状数组 线段树


交换次数即为逆序对数

poj1804数据范围小,int不会溢出,spoj上提价需用long long(注册spoj时,获取验证码时会访问谷歌,所以需要……)

#define _CRT_SECURE_NO_WARNINGS#include <cstdio>#include <cstring>const int maxn = 2e5 + 7;int a[maxn], b[maxn];long long cnt;//函数里有个全局变零cnt保存交换次数,即1逆序数。对ary[l:r]排序,函数结束后,ary = desvoid merge_sort(int ary[], int l, int r, int des[]) { //对ary[l..r]进行归并排序 if (l >= r) return;int mid = (l + r) >> 1;merge_sort(ary, l, mid, des);merge_sort(ary, mid + 1, r, des);int i = l, j = mid + 1, cur = l; //下面是合并while (i <= mid && j <= r) {if (ary[i] <= ary[j]) {des[cur++] = ary[i++];}else { //逆序啦,ary[i] > ary[j], ary[j]要调到前(左)面des[cur++] = ary[j++];cnt += mid - i + 1; //关键看这里//当ary[i] > ary[j]时,在前半部分中比ary[i]大的数都比ary[j]大,将ary[j]放在ary[i]前面的话,逆序数要加上mid + 1 - i}}while (i <= mid) {des[cur++] = ary[i++];}while (j <= r) {des[cur++] = ary[j++];}memcpy(ary + l, des + l, sizeof(int) * (r - l + 1));} int main(){int T;scanf("%d", &T);for (int k = 1; k <= T; ++k) {int n;scanf("%d", &n);cnt = 0;for (int i = 0; i < n; ++i)scanf("%d", a + i);merge_sort(a, 0, n - 1, b);//printf("%d", b[0]);//for (int i = 1; i < n; ++i)//printf(" %d", b[i]);//puts(""); //检验归并排序是否正确//printf("Scenario #%d:\n%d\n\n", k, cnt); //poj1804提交姿势,poj上数据范围小,int不会溢出printf("%lld\n", cnt); //http://www.spoj.com/problems/INVCNT/en/}return 0;}


BIT解法(适用于POJ1804)

#include <cstdio>#include <cstring>#define ADD 1000001#define M 2000001#define N 1007 using namespace std;int ary[N], bit[M + 1], n;int lowbit(int x) {return x & (-x);}void add(int pos, int val) {while(pos <= M) { //pos <= ?bit[pos] += val;pos += lowbit(pos);}}int sum(int pos) { //询问1:pos的和 int res = 0;while(pos > 0) {res += bit[pos];pos -= lowbit(pos);}return res;}int main() {int T, kase = 0;scanf("%d", &T);while (T-- > 0) {memset(bit, 0, sizeof bit);scanf("%d", &n);long long ans = 0; for (int i = 0; i < n; ++i) {scanf("%d", ary + i);ary[i] += ADD;ans += (i - sum(ary[i])); //这两句难理解 add(ary[i], 1);}printf("Scenario #%d:\n%lld\n\n", ++kase, ans);} return 0;}

hdu1394


#define _CRT_SECURE_NO_WARNINGS#include <cstdio>#include <algorithm>#include <cstring>using namespace std;const int N = 5007;int segTree[N << 1], ary[N];void build(int rt, int l, int r) {segTree[rt] = 0;if (l == r) return;int mid = l + r >> 1;build(rt << 1, l, mid);build(rt << 1 | 1, mid + 1, r);}void update(int rt, int l, int r, int pos) {if (l == r) {segTree[rt]++; //因为是求逆序数,所以加1return;}int mid = l + r >> 1;if (pos <= mid)update(rt << 1, l, mid, pos);else update(rt << 1 | 1, mid + 1, r, pos);segTree[rt] = segTree[rt << 1] + segTree[rt << 1 | 1]; //别忘了更新父节点}int query(int rt, int l, int r, int L, int R) {if (L <= l && r <= R) {return segTree[rt];}int mid = l + r >> 1, res = 0;if (L <= mid)res += query(rt << 1, l, mid, L, R);if (R > mid)res += query(rt << 1 | 1, mid + 1, r, L, R);return res;}int main(){int n;while (~scanf("%d", &n)) {memset(segTree, 0, sizeof segTree);int cnt = 0;for (int i = 0; i < n; ++i) {scanf("%d", ary + i);cnt += query(1, 0, n - 1, ary[i], n - 1);update(1, 0, n - 1, ary[i]); //动态更新}int minCnt = cnt;for (int i = 0; i < n; ++i) {cnt += n - ary[i] - ary[i] - 1; //这一步操作可以求得队首元素放到队尾的逆序数,前提是数组ary[i]是0:n-1的一个排列minCnt = min(minCnt, cnt);}printf("%d\n", minCnt);}return 0;}






阅读全文
0 0
原创粉丝点击