• [补题记录] Complete the Permutation(贪心、set)


    URL:https://codeforces.com/group/OcmZ7weh45/contest/487583/problem/J

    目录

    Problem/题意

    Thought/思路

    Code/代码


    Problem/题意

    给出一个长度为 N 的序列,其中的元素都是奇数。

    现在要求在两个奇数之间插入一个偶数,使得这三个数递增或递减。

    一共需要插入 N - 1 个偶数,并且要求这 N - 1 个偶数都不相同。

    Thought/思路

    显然这道题的关键就在于,两个奇数之间会有 >= 1 个偶数存在。

    我们将两个奇数视作一个区间(一共 N - 1 个区间,小的奇数作为左端点),那么我们在选择了某个偶数 X 后,很可能就会占用了其他区间仅有的一个偶数 X,使得条件不满足。


    (贪心)我们可以尝试先选择一个区间中最小的偶数,然后在其之后的区间也同样选择区间中还没有被选过的偶数中最小的哪个。


    问题就转换为,怎么保证每个区间选中的最小的偶数不会重复,这就要看我们是如何对这 N - 1 个区间排序的。

    (1)如果我们按照左端点优先递增来排序,就会出现左端点很小,右端点很大的情况,这意味着:明明这个区间可以选到一个很大的偶数,大到不会影响其他所有区间,而现在却因为先选择了可以选择的最小偶数,导致占用了排在它之后的区间唯一可以选择的偶数。

    举个例子:

    [1, 3] => 2

    [1, 5] => 4

    [3, 100000] => 6

    [5 7] => 选不了,错误

    (2)因此我们再考虑按照右端点优先递增来排序,显然由于有着右端点的限制,假设右端点严格递增,那么每个区间之间都一定能选到互异的偶数:比如 [1, 3]、[1, 5]、[1, 7] 选择 2、4、6。

    而就算前后两个区间右端点相同,由于我们在前面的区间里已经选完了可以选择的最小的偶数,因此后面这个区间肯定就是永远无法选到互异的偶数的,比如:[1, 3]、[1, 7]、[3, 7]、[5, 7]。

    综上,我们选择按照右端点优先递增来排序。


    核心思路就是上面这些,还有一个小问题:

    查找最小偶数的时候需要使用二分,当我们选择 set 保存所有的偶数时,二分时必须要使用 set 自带的 upper_bound,而不能使用 algorithm 的 upper_bound。前者二分复杂度 O(logn),后者二分复杂度 O(n),很容易超时

    Code/代码

    1. #pragma GCC optimize(2)
    2. #include "bits/stdc++.h"
    3. struct node {
    4. int x, y, id;
    5. bool operator < (const node &t) const {
    6. if (y == t.y) return x < t.x;
    7. else return y < t.y;
    8. }
    9. }p[200007];
    10. int a[200007];
    11. void solve() {
    12. int n; std::cin >> n;
    13. for (int i = 1; i <= n; ++ i) {
    14. std::cin >> a[i];
    15. if (i == 1) continue;
    16. int l = std::min(a[i], a[i - 1]), r = std::max(a[i], a[i - 1]);
    17. p[i - 1] = {l, r, i - 1};
    18. }
    19. std::sort(p + 1, p + n);
    20. std::set <int> st;
    21. for (int i = 2; i <= 2 * n - 2; i += 2) st.insert(i);
    22. bool flag = true;
    23. std::vector <int> ans(n);
    24. for (int i = 1; i <= n - 1; ++ i) {
    25. int l = p[i].x, r = p[i].y, id = p[i].id;
    26. //std::cout << "l=" << l << " r=" << r << " ";
    27. auto even = st.upper_bound(l);
    28. if (*even < r) {
    29. //std::cout << "even=" << *even;
    30. ans[id] = *even;
    31. st.erase(even);
    32. } else {
    33. flag = false;
    34. }
    35. //std::cout << "\n";
    36. }
    37. if (flag) {
    38. for (int i = 1; i <= n - 1; ++ i) std::cout << ans[i] << " ";
    39. } else {
    40. std::cout << -1;
    41. }
    42. std::cout << "\n";
    43. }
    44. signed main() {
    45. std::ios::sync_with_stdio(false);
    46. std::cin.tie(0); std::cout.tie(0);
    47. int t; std::cin >> t;
    48. while (t --) solve();
    49. return 0;
    50. }

  • 相关阅读:
    【Linux集群教程】02 高可用集群
    「C++小游戏教程」基本技巧(3)——发声函数 Beep()
    基于Java的家政公司服务平台设计与实现(源码+lw+部署文档+讲解等)
    卷积神经网络
    vue 性能优化方案
    WebStorm使用PlantUML
    ELK 7.17.5 集群部署及使用
    32个关键字详解①(C语言)
    2022杭电多校七 1007-Weighted Beautiful Tree(树形DP)
    商城系统功能需求分析_免费搭建方式介绍_OctShop
  • 原文地址:https://blog.csdn.net/joyride_run/article/details/134562539