hihocoder中的逆序排队

#1141 : 二分·归并排序之逆序对

题目如下:
----------------------------------
Time Limit: 10000ms
Case Time Limit: 1000ms
Memory Limit: 256MB
描述

在上一回、上上回以及上上上回里我们知道Nettle在玩《艦これ》。经过了一番苦战之后,Nettle又获得了的很多很多的船。
这一天Nettle在检查自己的舰队列表:

我们可以看到,船默认排序是以等级为参数。但实际上一个船的火力值和等级的关系并不大,所以会存在A船比B船等级高,但是A船火力却低于B船这样的情况。比如上图中77级的飞龙改二火力就小于55级的夕立改二。
现在Nettle将按照等级高低的顺序给出所有船的火力值,请你计算出一共有多少对船满足上面提到的这种情况。

提示:火力高才是猛!

输入

第1行:1个整数N。N表示舰船数量, 1≤N≤100,000
第2行:N个整数,第i个数表示等级第i的船的火力值a[i],1≤a[i]≤2^31-1。

输出

第1行:一个整数,表示有多少对船满足“A船比B船等级高,但是A船火力低于B船”。

Sample Input
10
1559614248 709366232 500801802 128741032 1669935692 1993231896 369000208 381379206 962247088 237855491
Sample Output
27
--------------------------------

这题是个典型逆序排队问题,经典解法就是用归并排序来求解。我采用的也是归并的方法,但是在hihocoder提交代码之后,一直提示wrong answer,但是自己用了多组测试用例都没发现有任何问题,简直太郁闷了。。。后来想到那要不用大数据方案测一下呗,于是设置了一个100000大的输入数组,发现返回结果为负数了。。。好,问题来了,返回负值,那不就是存储返回结果的变量不够长吗?真是智商捉急啊!于是将存储返回结果的int型变量变成了long long型的,一下就AC了。回想前几天做的一个微软的笔试题,当时提交答案后也是扣了一半分数,不明原因,现在回想估计有这个原因在里面,回头再去检查一下。

下面简单说一下思路:

在归并排序的过程中,当左右两组(分别长m, n)进行合并时,会临时申请一个m+n长的数组来存两者合并的有序数组。那么当扫面左边和右边数组时,如果发现右边比左边小,那么将右边数组中该元素放入临时数组中。如果发现左边数组大于或者等于右边数组中当前元素,那么将左边数组该元素放入临时数组中,同时更新逆序对的数量,增加的数量等于目前临时数组中的右边数组的元素个数(为什么这么认为呢,因为当前放入的左边数组元素大于临时数组中的所有元素,那么就与临时数组中的所有右边数组的元素构成了逆序对)。没图解释可能比较绕,本博客也只是对本人的一个记录,就将就这么写吧。

下面是实现代码:

</pre><pre name="code" class="cpp">#include <stdio.h>
#include <stdlib.h>

void merge(int *a, int p1, int p2, int p3, long long &sum) {
	if(p2 >= p3) return;

	int leftptr = p1;
	int midptr = p2;
	int *mergetmp = new int[p3-p1];
	int tmpIndex;

	for(tmpIndex = 0; leftptr < p2 && midptr < p3; tmpIndex++) {
		if(a[leftptr] <= a[midptr]) {
			mergetmp[tmpIndex] = a[leftptr];
			sum = sum + tmpIndex-(leftptr-p1);
			leftptr++;
		} else if(a[leftptr] > a[midptr]) {
			mergetmp[tmpIndex] = a[midptr];
			midptr++;
		}
	}
	if(leftptr == p2) { // left segement is shorter than the right
		for(int p=tmpIndex; p<(p3-p1); p++) {
			mergetmp[p] = a[midptr];
			midptr++;
		}
	} else if(midptr == p3) {
		for(int p=tmpIndex; p<(p3-p1); p++) {
			mergetmp[p] = a[leftptr];
			sum = sum + tmpIndex - (leftptr - p1);
			leftptr++;
			tmpIndex++;
		}
	}

	for(int p=0; p<p3-p1; p++) {
		a[p+p1] = mergetmp[p];
	}
	delete mergetmp;
}

int min(int a, int b) {
	return (a < b ? a : b);
}

void merge_c(int *a, int left, int right, int m, long long &sum) {
	if(m >= right - left) return;
	for(int i=0; i<right; i = i+m+m) {
		merge(a, i, i+m, min(i+m+m, right), sum);
	}
	merge_c(a, left, right, m+m, sum);
}

int main(int argc, char *argv[])
{
	int N;
	scanf("%d", &N);
	int *a = new int[N];

	for(int i=0; i<N; i++) {
		scanf("%d", a+i);
	}

	long long sum = 0;
	merge_c(a, 0, N, 1, sum);
	printf("%lld\n", sum);

	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值