CSP认证 202012-2 期末预测之最佳阈值
题目
解题思路
看到子任务中有m <= 10^5
,看到这里,我们必须条件反射地想到:如果用二重循环暴力求解,则一般会超时!
尝试以后果不其然,仅通过了70%的测试点,拿到70分。
那么,我们究竟如何优化,才能拿到全部的100分呢?
跟几位同学交流后,听到了很多使用一重循环解决的方法,也非常巧妙。下面我就介绍一下我在考场上想到的一种优化二重循环的方法,比较容易看懂。
- 因为如果用暴力二重循环,会重复很多不必要的遍历和比较操作(遍历阈值时,比如阈值3结束查询,阈值4开始查询,这时比3和4都小的数,以及比3和4都大的数,预测结果是不会变的,但暴力二重循环还是会再全部查询一遍,徒增运行时间!),所以我们有必要进行优化。
- 首先,用装有
pair
(就是数对)的容器vector
来储存输入数据,同时用集合set
存储可选阈值;(set
会随着元素的插入自动去重,以及自动排序,非常适合这道题) - 输入完成后,将
vector
按照每个pair
的第一个数从小到大排序; - 然后,计算当阈值为第一个可选阈值时,预测正确的个数
cnt
; - 维护一个用于遍历
vector
的索引index
。这样做的原因:在第一次计算完cnt
之后,遍历到剩余的可选阈值时,只需要关注前一个可选阈值与当前遍历到的可选阈值之间的数,计算这些数被预测正确的个数的变化。除此之外的数,要么小于前一个阈值,要么大于等于当前阈值,所以预测结果是不会变的! - 随后用
for
循环遍历剩余可选阈值,每轮都更新cnt
,并随时更新max_cnt
; - 因为
set
中的元素会自动从小到大排好序,所以这样取出的最佳阈值一定满足题目中“多个阈值均可以达到最高准确率时,选取其中最大的”这个条件!
源代码奉上!
C++代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <utility>
#include <set>
#include <algorithm>
using namespace std;
bool cmp(pair<int, int> a, pair<int, int> b)
{
return a.first < b.first;
}
int main()
{
int m, y, result;
scanf("%d", &m);
vector<pair<int, int> > v(m);
set<int> theta;
for (int i = 0; i < m; i++)
{
scanf("%d%d", &y, &result);
v[i] = make_pair(y, result);
theta.insert(y);
}
sort(v.begin(), v.end(), cmp);
int best = 0, max_cnt = -1, flag, cnt = 0, tmp;
set<int>::iterator it = theta.begin();
flag = *it;
int index = 0;
for (int i = 0; i < m; i++)
{
if (v[i].first < flag)
tmp = 0;
else
tmp = 1;
if (tmp == v[i].second)
cnt++;
if (v[i].first < flag)
index = i;
}
if (cnt >= max_cnt)
{
max_cnt = cnt;
best = flag;
}
for (set<int>::iterator it = ++theta.begin(); it != theta.end(); it++)
{
flag = *it;
while (v[index].first < flag) {
if (v[index].first < flag)
tmp = 0;
else
tmp = 1;
if (tmp == v[index].second)
cnt++;
else
cnt--;
index++;
}
if (cnt >= max_cnt)
{
max_cnt = cnt;
best = flag;
}
}
printf("%d", best);
return 0;
}