【问题标题】:How to run this code in less than 1 second?如何在不到 1 秒的时间内运行此代码?
【发布时间】:2015-11-15 22:49:36
【问题描述】:

如何在不超过时间限制的情况下解决此问题

http://codeforces.com/problemset/problem/474/B

我尝试将所有范围放入 2D 向量中,然后使用二进制搜索查找所需的索引,但似乎 fn BS() 中的循环需要大量执行,因为向量的大小可以是 10^6。

这是我的代码:

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

int Search(vector <vector<int> > a,int key){
    int start = 0;
    int end = a.size() - 1;
    while (start <= end){
        int mid = start + (end - start) / 2;
        if (a[mid][0] > key && a[mid][1] > key){
            end = mid - 1;
        }
        else if (a[mid][0] < key && a[mid][1] < key){
            start = mid + 1;
        }
        else {
            return mid;
        }
    }

    return -1;
}
vector <int> BS(vector <vector <int> > v, vector<int> keys){
    int j = 0;
    vector <int> piles;
    for (int i = 0; i < keys.size(); i++){
        piles.push_back(Search(v, keys[i])+1);
    }
    return piles;
}
vector < vector<int> > Range(vector<int> v){
    vector < vector<int> > ranges(v.size());
    int sum1 = 1;
    int sum2 = v[0];
    for (int i = 0; i < v.size(); i++){
        if (i == 0){
            ranges[i].push_back(sum1);
            ranges[i].push_back(v[i]);
            sum1 += v[i];
        }
        else{
            ranges[i].push_back(sum1);
            sum2 += v[i];
            ranges[i].push_back(sum2);
            sum1 += v[i];
        }
    }
    return ranges;
}

int main(){
    int n, m;
    cin >> n;
    vector <int> a, q;

    vector < vector <int> > v;
    for (int i = 0; i < n; i++){
        int k;
        cin >> k;
        a.push_back(k);
    }
    cin >> m;
    for (int i = 0; i < m; i++){
        int l;
        cin >> l;
        q.push_back(l);
    }

    v = Range(a);

    vector <int> jucy = BS(v, q);


    for (int i = 0; i < jucy.size(); i++){
        cout << jucy[i] << endl;
    }




}

【问题讨论】:

  • 现在运行速度有多快?
  • 当 n 接近 10^5 时,它在测试用例 3 的运行时间超过 1000 毫秒
  • 在每次调用时不要不必要地复制输入向量(参数)怎么样? wtb参考。
  • 尽可能减少循环使用。与其他算法相比,线性循环(while 循环和从 A 到 Z 的 for 循环)非常糟糕。如果你上一门数据结构和算法课程,你会学到这一点。
  • 您使用了错误的数据结构和错误的算法。查看评分最高的答案,它使用正确的数据结构和正确的算法。

标签: c++ search binary


【解决方案1】:

事实上,我认为您根本不需要 2D 矢量,您只需要 1D。例如,看起来像这样 [2,9,12,16,25],每个桩的上限,你可以很容易地构建这个。然后,对于每一个多汁的蠕虫,您都以这种方式进行二进制搜索,它返回的索引值大于或等于您正在寻找的值。你搜索得到的索引就是你要找的那一堆。

一些伪代码:

A[n] - vector of upper bounds

A[0] = a0

For each 0<i<=n A[i]=A[i-1]+ai

For each q do std lower_bound on A looking for q,

你得到的索引是第一个值等于或大于q,所以是q的堆。

和 C++ 代码:

#include <iostream>
#include <vector>
#include <algorithm>
#include <iostream>

using namespace std;

int main()
{
    int n, m;
    cin >> n;

    vector<int>A;
    A.resize(n);

    int ai;
    cin >> ai;
    A[0]=ai;

    for (int i = 1; i < n; i++){
        cin >> ai;
        A[i]=A[i-1]+ai;
    }

    cin >> m;
    int q;
    for (int i = 0; i < m; i++){
        cin >> q;
        cout << std::distance(A.begin(),std::lower_bound(A.begin(),A.end(),q))+1<<endl;
    }

    return 0;
}

你必须给距离加上 +1,因为桩是从 1 开始编号的。为这个例子工作,看起来很快。

【讨论】:

  • 如果你没有发布这个,我会发布的。这是显而易见的答案。这里不需要向量的向量。构建A 是O(n),搜索q_im 值是O(m*log(n))。快速简单。
  • 非常好,std::lower_bound 为所需的二分搜索提供了完全正确的逻辑。 Codeforces.com 对问题的时间要求足够宽松,perl 解决方案可以通过。 (使用我们都想到的这个相当明显的解决方案。)
  • 顺便说一句,您可以通过将旧的 ai 保存在临时变量中来节省几个周期,而不是为其引用 A[i-1]。 amd64 上的g++ -O3%rbx 中保留指向A 中当前位置的指针,在每次调用cin &gt;&gt; 之后使用之前加载A[i-1]。我猜它无法证明函数调用不会修改A[i-1]。是的,我知道函数调用比从 L1 缓存中保存负载要多几个数量级,我只是觉得这很有趣,因为我一直在看 asm。
【解决方案2】:

最明显的优化机会是,而不是使用 vector&lt;vector&lt;int&gt;&gt;,而是使用 vector&lt;int&gt; 并将 2D 索引手动调整为 1D。您可以编写一个简单的包装类来为您执行此操作。

这样会更快的原因是所有内存都将被分配为一个连续的单元。如果你有一个向量向量,那么每一行都会在其他地方,你会有很多缓存未命中。

这是一个代码示例:

struct 2D_Vector {
  std::vector<int> me_;
  int ncols_;
  2D_Vector(int nrows, int ncols) : me(nrows * ncols), ncols_(ncols) {}

  int & get(int y, int x) { return me_[y * ncols_ + x]; }
  const int & get(int y, int x) const { return me_[y * ncols_ + x]; }

  ...
};

如果你预先分配了它需要的所有空间,那么它应该非常有效地使用内存。

此外,通过值而不是通过引用传递大型函数参数是非常浪费的,因为它会导致生成和销毁不必要的副本。 (就像 WhozCraig 指出的那样。)

【讨论】:

  • 我不明白如何将 2D 索引调整为 1D?
  • @JoeEhab12:在您的情况下,您应该使用struct { int start, end; } 的向量而不是向量的向量。 (其实你应该已经注意到了一堆的末端和下一个的开始之间的冗余,并且只使用了一个向量。)
  • @JoeEhab12:但要回答您的问题,索引为[i][j] 的矩形二维数组在内存中与索引为[i*n + j] 的线性数组在内存中看起来相同。但是,向量的向量与 2D 数组不同:每个元素可以是不同的长度,因此它可以是 ragged 数组。如果您不打算利用这一点,请不要使用它,因为这意味着外部向量正在存储指针(以及长度和保留空间量),而不是直接存储数据。所以有一个额外的指针追踪步骤来访问v[i][0]
猜你喜欢
  • 2014-07-17
  • 2016-07-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-03
  • 2012-02-21
  • 1970-01-01
相关资源
最近更新 更多