1. 题目描述
2. 一开始的思路(会超时)
最直白的思路就是直接模拟,然后每次都进行一遍 sort。
1 |
|
思路直白简单,但是会超时。
2. 分析一波
2.1 超时的原因
为啥会超时?这当然是废话了,题目规定了时间限制,超过了就是超过了呗。但是我们得反思一下,既然代码没有问题,是不是意味着还有更好的思路呢?
首先我们来想想,到底是哪里浪费的时间。显然当然是 sort
了!可为什么会浪费时间呢?我们注意到,在这个题目中 participants
无论在哪轮比赛后,都有一个特点,那就是它必然是交错有序的。换而言之,如果我们单独把胜者和败者分别拿出来然后按照原来的顺序排列起来,那这两个序列都是有序的。
明白了这一点,我们也就不难理解为什么 std::sort
表现不佳了。毕竟本来这个序列并没有那么无序,我们却非要用对待随机序列那样进行排序,当然会做很多无用的操作了!
2.2 使用 merge 改善算法
既然这样,我们就要思考一种更好的方法。看到两个有序的子序列,我们自然而然能联想到归并排序了(并不,我也是看了别人的解答才想到的)。如果我们把两个有序序列分开然后最后再合并起来,这不就大大提高效率了吗?
对时间复杂度进行简单的分析,原本的算法主体部分如下: 1
2
3
4
5
6
7
8sort;
for i = 1 : R with step = 1
{
for (pair in participants)
compete;
modify score in participants;
sort;
}
复杂度为 \(O(n\log{n}) + R\times (O(N) + O(n\log{n}))\)。
修改算法为: 1
2
3
4
5
6
7
8sort;
for i = 1 : R with step = 1
{
for (pair in participants)
compete;
split to winners + losers;
merge(winners, losers) to participants
}
复杂度为 \(O(n\log{n}) + R\times (O(N) + O(N))\)。
时间复杂度一下子降了好多!至于空间复杂度。。。谁在乎呢!
那么怎么 merge 呢?我们当然可以自己手写,不过最好的办法是直接用 std::merge
,它就在 <algorithm>
头文件里。
1 | template <class InputIterator1, class InputIterator2, class OutputIterator, class Compare> |
意思是:把 [first1, last1)
和 [first2, last2)
合并到 result
开头的空间里,最后一个参数是比较器。
3. AC 算法
1 |
|
4. 感言
以后不要看到排序就无脑 std::sort
,对于这种可以划分为若干个有序序列的情况,用 ,erge
显然更加高效。