题目背景
考虑到安全指数是一个较大范围内的整数、小菜很可能搞不清楚自己是否真的安全,顿顿决定设置一个阈值 ,以便将安全指数 转化为一个具体的预测结果——“会挂科”或“不会挂科”。
因为安全指数越高表明小菜同学挂科的可能性越低,所以当 时,顿顿会预测小菜这学期很安全、不会挂科;反之若 ,顿顿就会劝诫小菜:“你期末要挂科了,勿谓言之不预也。”
题目链接
终于理解了这道前缀和的题目啦!想不到一年后的我再次进化,此题直接拿下!
具体的思路就是运用前缀和的思想,大大降低了时间复杂度,如果用暴力法,时间是 n2,是必然会超时的,所以我们使用巧妙的思路。
首先定义结构体,存储每个位对应的两个数,然后需要先将 结构体按照阀值从小到大的循序进行排序,这样子在定义最佳阀值的时候,比大的都在右侧,比小的都在左侧,计算前缀和,由于结果不是 0 就是 1,所以,1 的个数就是前缀和的个数,0 的个数就是总的个数减去 1 的个数。
然后开始进行求解,这里使用一个集合 set 来去除重复的元素,至于为什么要去重,分析在下面。
开始求解,由于右边的数字都比 阀值大,所以结果是 1 的还是预测正确,只需要计算 sum[n] - sum[i - 1] 即可
左边的阀值都小,所以只需要计算 0 的个数,只需要让总个数 i - 1 减去 1 的个数 sum[i - 1] 即可
每次如果遇到大的就更新,由于 阀值从小到大,所以最后的最佳阀值就是最大的。
至于为什么要去重,我有一些理解,如果不去重会有错误的情况。
举个例子;
0 1
1 0
1 1
3 1
此例子中,阀值 1 出现了两次,由于 大于等于 1 的result 结果都是 1才正确,所以此时大于等于 1 的结果,是 2,但是如果不去重,下一个阀值还是 1,此时小于 1 应该是 0 才对,但是我们计算了 i - 1 - sum[i - 1] 是将该索引前面所有的 0 都加上了,此时的 前面的 1 不满足题意,所以就错误了
在画个图分析一下吧
至于为什么要去重,我这里再分析一下:
比如题目中的例子
#include<iostream>
#include<string>
#include<algorithm>
#include<bits/stdc++.h>
#include<stack>
#include<set>
#include<vector>
#include<map>
#include<queue>
#include<deque>
#include<cctype>
#include<unordered_set>
#include<unordered_map>
#include<fstream>
#include<cstring>
using namespace std;
const int N = 100010;
struct Node {
int y;
int result;
};
bool cmp(Node a, Node b) {
return a.y < b.y;
}
Node a[N];
set<int> st;
int sum[N];
int main() {
int n;
int max_num = -1;
int res = 0;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i].y >> a[i].result;
}
sort(a + 1, a + n + 1, cmp);
for (int i = 1; i <= n; i++) {
sum[i] = sum[i - 1] + a[i].result;
}
for (int i = 1; i <= n; i++) {
int dns = a[i].y;
if (st.count(dns)) {
continue;
}
st.insert(dns);
int right = sum[n] - sum[i - 1];
int left = i - 1 - sum[i - 1];
int sum = right + left;
if (sum >= max_num) {
max_num = sum;
res = dns;
}
}
cout << res;
}