试题 算法训练 逆序数奇偶
资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
老虎moreD是一个勤于思考的青年,线性代数行列式时,其定义中提到了逆序数这一概念。不过众所周知我们只需要知道逆序数的奇偶性就行了,为了防止计算上的失误,moreD准备编写一个小程序来判定。只要判断奇偶性就行了哦!
另外作为一个技术宅,moreD对线性代数中最小下标为1非常不满,于是所有下标均从0开始。
输入格式
一个测试点包含多组数据,你需要不断读入直到输入结束。
每组数据第一行为一个n,接下来第二行输入n个数字,是一个0到n-1的排列。
输出格式
输出若干行,每行表示对应组数据逆序数奇偶性,奇数输出odd,偶数输出even。
样例输入
5
0 1 2 3 4
5
4 3 1 2 0
样例输出
even
odd
数据规模和约定
设每组测试点T个数据
1<=T<=10
1<=n<=100000
这道题看上去没什么思路,首先想到的是暴力,暴力的话这道题便可以轻松的写出来
#include<iostream>
#include<vector>
using namespace std;
int a[100001];
int main(void)
{
int n;
while (cin >> n) {
for (int i = 0; i < n; i++)
cin >> a[i];
int ans = 0;
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++)
if (a[j] < a[i])
ans++;
}
if (ans & 1)
cout << "odd" << endl;
else
cout << "even"<<endl;
}
return 0;
}
很明显会超时
暴力的话时间复杂度达到了n*n,根据题目的数据集,最大为1000000,必然会超时
该如何去减少复杂度?
可用归并思想
对数组进行归并排序,在每次“并”时,对数组进行比较,找到左数组中每个元素在右数组中第一个比其大的数,则其逆序数便为右下标到中间的距离。
直接看代码
#include<iostream>
#include<vector>
using namespace std;
int a[100001];//用于储存输入的数组
int temp[100001];//copy数组时使用
int mergeSort(int a[], int left, const int& right) {
//定义终止条件
if (left >= right)
return 0;
//求中间值
int mid = (left + right) >> 1;
//不断进行递归,使得数组呈现有序递增
int inv_count = mergeSort(a, left, mid) + mergeSort(a, mid + 1, right);
//i为左数组下标,j为右数组下标,pos为临时数组下标
int i = left, j = mid + 1;
int pos = 1;
while (i <= mid && j <= right) {
//如果a[i]<a[j],那么A[j]时右数组中第一个大于a[i]的元素,a在左右两数组是升序的,
//那么从mid+1到j-1的元素都小于a[i],则a[i]逆序数为j-mid-i
if (a[i] <= a[j]) {
temp[pos] = a[i];
++i;
inv_count += (j - mid) - 1;
}
else {
temp[pos] = a[j];
++j;
}
pos++;
}
//左数组是否全部遍历
for (int k = i; k <= mid; k++) {
temp[pos++] = a[k];
inv_count += (j - mid) - 1;
}
//右数组是否全部遍历
for (int k = j; k <= right; k++)
temp[pos++] = a[k];
pos = 1;
//复制数组
for (int k = left; k <= right; k++,pos++) {
a[k] = temp[pos];
}
return inv_count;
}
int main(void)
{
int n;
while (cin >> n) {
for (int i = 0; i < n; i++)
cin >> a[i];
int ans = mergeSort(a, 0, n - 1);
//判断奇偶
if (ans & 1)
cout << "odd" << endl;
else
cout << "even" << endl;
}
return 0;
}
通过递归使得时间复杂度变为log N
再次提交,看结果