C++逆序数(奇排列和偶排列的判定)

逆序数简介

在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。一个排列中所有逆序总数叫做这个排列的逆序数。也就是说,对于n个不同的元素,先规定各元素之间有一个标准次序(这里规定从小到大为标准次序),于是在这n个元素的任一排列中,当某两个元素的先后次序与标准次序不同时,就说有1个逆序。一个排列中所有逆序总数叫做这个排列的逆序数。

而逆序数就是判定一个排列为奇排列还是偶排列的重点。如果逆序数为奇数,则此排列为奇排列,否则为偶排列。

举个例子,看53124这个序列,5在3前面,5在1前面,5在2前面,5在4前面,3在1前面,3在2前面,逆序数为6,故此排列为偶排列。

逆序数计算

如果直接模拟的话时间复杂度为On^2,但代码就简单的多了,可如果你想优化时间,那么就得使用到归并排序了。

:如果你尚未学习过归并排序,可以浏览此网页来学习
https://blog.csdn.net/SkeletonKing233/article/details/100676151

而逆序数的计算在我们将两个子序列合并的时候累加就可以了。

举个例子,如果我们要将这两个子序列合并的话
1,4,6,7,9
2,3,5,10,13,21
当第一个序列枚举到第2个元素4,第二个序列枚举到第1个元素2时,4 > 2为逆序,我们需要进行累加,又因为两个子序列都是有序的所以如果4 > 2,那么第一个子序列从第2个元素4开始后面的所有数都必然比2大,所以让逆序数的值加上(mid - i + 1),既从第2个元素到第5个元素中元素的个数。这样时间复杂度就减少为On * log2(n)级别的了。

(PS:NR是指数组元素个数的上限)

# include <cstdio>
# include <iostream>
# include <cmath>
# include <cstring>
# include <algorithm>
using namespace std;

# define FOR(i, a, b) for(int i = a; i <= b; i++)
# define _FOR(i, a, b) for(int i = a; i >= b; i--)

const int NR = 100000;

int n, cnt;
int a[NR + 10];

void MergeArray(int left, int mid, int right){
	int ans[NR + 10], len = 0;
	int t1 = left, t2 = mid + 1;
	while(t1 <= mid && t2 <= right){
		if(a[t1] < a[t2]) ans[++len] = a[t1++];
		else{
			ans[++len] = a[t2++];
			cnt += mid - t1 + 1;
		}
	}
	while(t1 <= mid) ans[++len] = a[t1++];
	while(t2 <= right) ans[++len] = a[t2++];
	FOR(i, left, right) a[i] = ans[i - left + 1];
}

void MergeSort(int left, int right){
	if(left >= right) return;
	int mid = (left + right) / 2;
	MergeSort(left, mid);
	MergeSort(mid + 1, right);
	MergeArray(left, mid, right);
}

int main()
{
	scanf("%d", &n);
	FOR(i, 1, n) scanf("%d", &a[i]);
	MergeSort(1, n);
	printf("此排列逆序数为%d\n", cnt);
	if(cnt % 2 == 1) puts("此排列为奇排列");
	else puts("此排列为偶排列");
	return 0;
}

God Bless You For Ever!

发布了33 篇原创文章 · 获赞 47 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/SkeletonKing233/article/details/99622727