问题描述:
多人排成一个队列,我们认为从低到高是正确的序列,但是总有部分人不遵守秩序。如果说,前面的人比后面的人高(两人身高一样认为是合适的),那么我们就认为这两个人是一对“捣乱分子”。
比如说,现在存在一个序列: 176, 178, 180, 170, 171
这些捣乱分子对为:<176, 170>, <176, 171>, <178, 170>, <178, 171>, <180, 170>, <180, 171>
那么,现在给出一个整型序列,请找出这些捣乱分子对的个数(仅给出捣乱分子对的数目即可,不用具体的对。
要求:
输入:
为一个文件(in),文件的每一行为一个序列。序列全为数字,数字间用”,”分隔。
输出:
为一个文件(out),每行为一个数字,表示捣乱分子的对数。
详细说明自己的解题思路,说明自己实现的一些关键点。
并给出实现的代码,并分析时间复杂度。
限制:
输入每行的最大数字个数为100000 个,数字最长为6 位。程序无内存使用限制。
解决思想:
研究题意,其实就是让我们找到所有大于当前数且在其之前出现的数的个数,当读懂题意后,我首先想到的是采用BF来找出所有的捣乱份子对,即针对每一个数a[i],查找a[0...i-1]中的每一个数,如果比它大,则是捣乱分子,记录增1,直到所有的数遍历完。采用该方法空间复杂度为O(1),但时间复杂度O(N^2)。
由于题目要求不限制内存的使用,可考虑采用更高效的方法来降低时间复杂度,即空间换时间。想到这里,大家一定想到了那种排序方法既是稳定的,其时间复杂度又是O(N*lgN),对,合并排序。
本题也可采用合并排序思想来求解,同时也引入了分治的思想:
(1)先将待求解的元素划分为两个较小部分,分别求得两部分的捣乱分子对的个数,同时对元素进行排序。
(2)对已排序的两个部分进行合并,在合并时如果后半部分的值位于前半部分(插入的位置),则捣乱分子对增加j - low - k(见源码)。
(3)返回所求得得捣乱分子对个数。
通过合并于分治思想,空间复杂度为O(N),时间复杂度降低到O(N*lgN)。
以下部分为源码(已测):
int count_trouble_makers(int *array, int low, int high)
{
if (low >= high)
{
return 0;
}
int count, halfpart;
halfpart = (low + high) / 2;
count = count_trouble_makers(array, low, halfpart) + count_trouble_makers(array, halfpart + 1, high);
int i, j, k, *tarray;
tarray = new int[high - low + 1];
for (k = 0, i = low, j = halfpart + 1; i <= halfpart && j <= high;)
{
if (array[i] > array[j])
{
tarray[k] = array[j];
count += j - low - k;
++j;
++k;
} else {
tarray[k++] = array[i];
i++;
}
}
while(i <= halfpart) {
tarray[k++] = array[i++];
}
while(j <= high) {
tarray[k++] = array[j++];
}
for (k = 0, i = low; i <= high; ++i, ++k)
{
array[i] = tarray[k];
}
delete []tarray;
return count;
}