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

25 篇文章 0 订阅
23 篇文章 0 订阅

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


交换次数即为逆序对数

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 = des
void 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]++; //因为是求逆序数,所以加1
		return;
	}
	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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值