POJ 1804 逆序对问题【分治】【线段树】【树状数组】【平衡树】

至于为何冒泡排序的次数,是逆序对的数量?这里说一下从小到大冒泡的问题。对于一个数字k(一开始在数组的k的位置), 在冒泡排序中,只有出现有一个数字在a[k]前面,并且比a[k]要大,这个数字才会交换位置。并且只会向前交换。显然,对于排序结束后的数列, a[k]前面是不会有比他大的数字了, 同时!a[k]只会和在他前面,比他大的数字交换(冒泡排序中,交换位置的判定
摘要由CSDN通过智能技术生成

至于为何冒泡排序的次数,是逆序对的数量?


这里说一下从小到大冒泡的问题。


对于一个数字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


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值