关于期末预测之最佳阈值一题的总结

前言:下面的题目是CCFCSP第21次认证的第二题,从总体上看只要理解好题目意思还是能够顺利做出来的。但是这道题想要拿满分还是没有那么容易的。在这里插入图片描述
在这里插入图片描述 比较容易想到是使用双层for循环遍历,第一层遍历所有的可能阈值,第二层遍历所有的输入数据,然后计算出正确次数最多的阈值即可。
上面的算法思路其实是基于暴力法,算法的时间复杂度达到o(n^2)而这种算法的缺陷就是如果数据量过大就会很耗时。再看看题目给的子任务:
在这里插入图片描述
可以看出有30%的数据可能达到10^5,所以如果我们用暴力法提交,最后很有可能有30%的测试数据运行超出时间。
此时我们应该考虑如何优化暴力法。比较常见的思路观察暴力法是否有重复计算的数据经过思考,我们发现,其实当我们计算好最小一个阈值(记作a)的正确次数后,与它相邻的下一个阈值(记作b)其实对于大部分数据的结果和a是一样的,只有当数据的第一个量是a时(或者说是上一个阈值时),结果才会不同。拿样例一举个例子,此时a=0,b=1,计算完a的正确次数是4后,接着计算b,此时b对于后面的5个数据结果都和a一样,只有第一个数据的结果不一样,对于a第一个数据预测错误,对于b第一个数据正确,因此可以算出b的正确次数 = a的正确次数 + 1;因此,对于所有的阈值,我们只需要知道上一个阈值的正确次数,然后再计算数据集中的第一个量是上一个阈值的数据就能计算出这个阈值的正确次数。转念一想,是不是很想我们熟悉的递归大法,已知n=1时的结果,已知所有的后一个数据与其前面的数据的对应关系,我们就能计算出所有数据的结果。其实也完全可以写一个递归函数求出所有阈值对应的正确次数,不过题目没有要求就不用多次一举了。
这里讲一下优化暴力法的核心代码,以便大家写出正确的100分答案。首先我们根据第一个变量大小对数据集排好序,优化暴力法的核心也是两层循环,第一层是从小到大遍历所有阈值(除了最小的阈值,最小的阈值的正确次数先用一个循环遍历数据集求得),第二层是一个while循环,遍历从上上个阈值对应的数据到上个阈值的对应的数据。优化后的暴力法的时间复杂度只有0(2n).这个o(2n)怎么得出的呢,其实我们只要理解了原理后就会知道,两层循环下只是遍历了一遍数据集,加上求第一个阈值的正确的次数遍历了一次数据集,所以总共遍历了两边数据值。

源码附上

#include <iostream>
#include <set>
#include <vector>
#include <algorithm>
using namespace std;

bool cmp(pair<int, int> a, pair<int, int> b) {
	return a.first < b.first;
}
int main() {
	vector<pair<int,int>> ans;
	int n;
	cin >> n;
	vector<pair<int, int>> pir;
	set<int> st;
	for (int i = 0; i < n; i++) {
		int first, second;
		cin >> first >> second;
		st.insert(first);
		pir.push_back(pair<int, int>(first, second));
	}
	sort(pir.begin(), pir.end(), cmp);
	set<int>::iterator it = st.begin();
	int temp_First = *it;
	int temp_ans = 0;
	for (auto p : pir) {
		if (p.first >= temp_First && p.second == 1) temp_ans ++;
		if (p.first < temp_ans && p.second == 0) temp_ans ++;
	}
	
	ans.push_back(pair<int, int>(temp_First, temp_ans));
	int index = 0;
	for (++it; it != st.end(); it++) {
		int count = 0;
		int temp = *it;
		while (pir[index].first < temp) {
			if (pir[index].second == 1) temp_ans--;
			else temp_ans++;
			index++;
			if (index == pir.size()) break;
		}
		index -= count;
		ans.push_back(pair<int,int>(temp , temp_ans));
	}
	int result = 0;
	int result_index = 0;
	for (int i = 0; i < ans.size(); i++)
	{
		if (ans[i].second >= result) result = ans[i].second;
	}
	for (int i = 0; i < ans.size(); i++) {
		if (ans[i].second == result) result_index = ans[i].first;
	}
	cout << result_index;
	return 0;
}

总结:暴力法很容易想到,但是面对大的数据集往往会超出运行时间,优化的一个思路就是观察暴力法重复计算的数据,找到隐藏的规律,结合例子去思考并优化。
PS:如果有什么错误的地方,或者有什么疑问欢迎评论区指出,此外,我也会持续更新关于我刷题的一些总结(主要是leetcode和CSP认证里面的题目

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值