【问题标题】:What is complexity of my code and how can I improve it?我的代码的复杂性是什么?如何改进它?
【发布时间】:2020-09-03 17:24:36
【问题描述】:

这是来自 CSES 问题集的问题。

有 n 个申请人和 m 个免费公寓。您的任务是分配公寓,以便尽可能多的申请人获得公寓。

每个申请人都有自己想要的公寓大小,他们会接受任何与期望大小足够接近的公寓。

输入

第一行输入三个整数n、m、k: 申请人、公寓数量和允许的最大数量 区别。

下一行包含 n 个整数 a1,a2,…,an:所需的公寓 每个申请人的大小。如果申请人的期望大小是 x,他 或者她会接受任何大小在 x−k 和 x+k 之间的公寓。 最后一行包含 m 个整数 b1,b2,…,bm:每个公寓的大小。

输出

 Print one integer: the number of applicants who will get an apartment.

约束

1 ≤ n, m ≤ 2e5
0 ≤ k ≤ 1e9
1 ≤ ai, bi ≤ 1e9

我的尝试

#include <iostream>
#include <set>
#include <vector>
#include <algorithm>
#include <utility>
typedef long long int ll;

using namespace std;

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    int n, m, k;
    cin >> n >> m >> k;
    vector<int> vn, vm;
    for (int i = 0; i < n; i++)
    {
        int p;
        cin >> p;
        vn.push_back(p);
    }
    for (int i = 0; i < m; i++)
    {
        int p;
        cin >> p;
        vm.push_back(p);
    }
    int c = 0;
    sort(vn.begin(), vn.end());
    sort(vm.begin(), vm.end());
    for (int i = 0; i < m; i++)
    {

        for (int j = 0; j < vn.size(); j++)
        {
            if (vm[i] <= vn[j] + k && vm[i] >= vn[j] - k)
            {
                c++;
                vn.erase(vn.begin() + j);
                break;
            }
            if (vn[j] < vm[i] - k)
            {
                vn.erase(vn.begin() + j);
                j--;
                continue;
            }
            if (vn[j] > vm[i] + k)
            {
                break;
            }
        }
    }
    cout << c << "\n";
}

我在某些测试用例中超出了时间限制。

【问题讨论】:

  • 在实施解决方案之前,您应该真正弄清楚解决方案的时间复杂度,这样您就不会浪费时间实施永远不够快的东西。乍一看,您的代码似乎至少是O(n^2)O(nm),这对于n, m &lt;= 2e5 来说绝对不够快。
  • 我想知道你能不能把两个 5 号的住户放在一个 10 号的公寓里?
  • 如果你有一个严格的“一个公寓 - 一个居住者”规则,只需将你的居住者和公寓放入一个数组中,排序 (O(NlogN) 并进行一次传递 (O(N) ) 使用简单的if-then-else 逻辑找出公寓-居住者对。
  • 你能用文字描述你的算法,把代码归为备用解释吗?根据该描述,您预计复杂度是多少?

标签: c++ algorithm sorting search


【解决方案1】:

我的解决方案刚刚被接受

想法:
  • 对于每个申请人,根据k计算他们各自的范围。
  • 删除永远不会被分配到任何给定公寓的申请人
  • 对申请人的范围进行排序
  • 对公寓进行分类
  • 现在对于每个公寓,找到范围包括公寓大小的第一个申请人
  • 一旦找到申请人,将他/她分配到当前公寓,并将他/她从下一公寓的候选人中删除
Python 解决方案
def solve(applicants, apartments, k):
    apartments.sort()
    dapps = sorted([(applicant-k, applicant+k)
                    for applicant in applicants if applicant - k <= apartments[-1]], reverse=True)
    cnt = 0

    for dep in apartments:
        while dapps and dapps[-1][1] < dep:
            dapps.pop()
        if dapps and dapps[-1][0] <= dep <= dapps[-1][1]:
            cnt += 1
            dapps.pop()
    return cnt
  • 时间复杂度:O(NlogN + MlogM)
  • 空间复杂度:O(N)

【讨论】:

    【解决方案2】:

    您可以使用 find + Priority Queue 来解决这个问题。 拥有一个以所有 bx 作为值的最大优先级队列。 使用 ax 在优先级队列中查找(参考 Priority Queue with a find function - Fastest Implementation)。 在优先队列比较器函数中,检查 (bx >= ax - k) && (bx

    【讨论】:

      【解决方案3】:

      您的算法的时间复杂度是 O(m*n^2),但实际上我们可以说它是 O(n^3)。

      供您参考,它是 O(n^3),因为您有两个 for 循环,产生了 O(n^2) 的复杂性,但是在这些循环中,有一个函数调用 std::vector.erase( ),擦除函数是一个线性运算,O(n) 所以我们将它乘以 O(n^2) 并得到 O(n^3) 的时间复杂度。如果您需要更清楚地说明这一点,请与我联系。

      现在谈谈理想运行时的问题。这显然是一个排序/搜索问题。我相信这可以在 O(n^2) 内完成。

      我的解决方案:

      按升序对公寓大小和所需的每个申请人进行排序。 按顺序填满公寓,从最挑剔的“租户”开始,即所需大小最小的那些。请注意,这是“O(n^2)”,但实际上比标准的 O(n^2) 算法快得多。

      伪代码:

              int count = 0;
              vector<int> desired, apartments;
              //Fill these vectors
              //Sort the vectors
              for(int apt = 0; apt < apartments.size();apt++){
                  for(int person = 0;person < desired.size();person++){
                     if(canEnterApartment(desired.at(person),apartments.at(apt)){
                       count++;
                       //Remove all tenants before this
                       break;
                     }
                  }
               }
               return count;
      

      【讨论】:

      • OP 的代码复杂度其实比这要少。您假设 erase 始终是线性的,但您忘记考虑它会减小向量的大小。这意味着它总共只能调用O(n) 次,因此擦除操作的复杂度是O(n^2)。其余代码的复杂度为O(nm),因此总体上为O(n(n + m))O(n^2 + nm)。在这种情况下仍然不够好,但有时这样的差异很重要。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-12-07
      • 1970-01-01
      相关资源
      最近更新 更多