题意:
给定一个数组,你可以对数组进行 若干次操作,每次操作可以 删除两个数字并添加一个数,求 1 2 3
连续自然数的最后一个值最大。
思路:
先将输入的元素 放入 set < int > tr
中进行去重,显然 去重后的元素个数为 tr.size()
。
贪心策略:
从前往后遍历每个 i ∈ [1, +∞)
,如果 i
在 tr
中存在,则 直接将其删除。
如果不存在:
-
优先删除 “盈余元素”,总元素个数为
n
,去重后元素个数为tr.size()
,则 盈余元素数量为cnt = n - tr.size()
。这里删的时候无需真正删除,只需要 每次将cnt
和tr
减少对应次数即可(当cnt >= 2
时,对应cnt -= 2
,此时tr
无需删除元素。当cnt == 1
时,对应cnt -= 1
并 将tr
中最大的元素删除)。 -
当 “盈余元素” 删除完成后,即
cnt == 0
时,判断tr
中的元素是否大于等于2
,如果 是,则 删除tr
中最大的两个元素,如果 不是,则表示 无法继续遍历当前i
,则直接 输出i - 1
。
(上面的 删除 操作 每次都要保证恰好删除 2
个元素)
使用 set
时 注意事项:
set
不仅能 以 元素值 作为 erase
时的索引,还可以以 迭代器 作为 索引,这点很强大,本题中两者都用到了。
题中涉及到了 将 set
中最大的两个元素删除,对应到代码上就是:
//前提是保证 tr.size() >= 2
auto pp = tr.end(); --pp; tr.erase(pp);
auto qq = tr.end(); --qq; tr.erase(qq);
而不能写成下面的形式,输入数据时会报错,即 不能将一个迭代器连续递减两次
//前提是保证 tr.size() >= 2
auto pp = tr.end(); --pp; tr.erase(pp);
--pp; tr.erase(pp);
时间复杂度:
O ( n l o g n ) O(nlogn) O(nlogn)
代码:
#include <bits/stdc++.h>
using namespace std;
//#define int long long
//#define map unordered_map
const int N = 3e5 + 10;
int a[N];
inline void solve()
{
int n; scanf("%d", &n);
set<int> tr;
for (int i = 1; i <= n; ++i)
{
scanf("%d", &a[i]);
tr.insert(a[i]);
}
int cnt = n - tr.size();
for (int i = 1; ; ++i)
{
auto p = tr.find(i);
if (p == tr.end())
{
if (cnt >= 2) {
cnt -= 2;
}
else if (cnt == 1 && tr.size() >= 1) {
--cnt;
auto pp = tr.end(); --pp; tr.erase(pp);
}
else if (cnt == 0 && tr.size() >= 2) {
auto pp = tr.end(); --pp; tr.erase(pp);
auto qq = tr.end(); --qq; tr.erase(qq);
}
else {
printf("%d\n", i - 1);
return;
}
}
else
{
tr.erase(p);
}
}
}
signed main()
{
// ios::sync_with_stdio(false);
// cin.tie(nullptr), cout.tie(nullptr);
int _ = 1; //cin >> _;
while (_--)
{
solve();
}
return 0;
}