如何从矢量或集合中更有效地擦除元素?

时间:2017-04-08 16:42:52

标签: c++ algorithm vector set

问题陈述:

输入:

前两个输入是整数n和m。 n是在比赛中战斗的骑士的数量(2&lt; = n&lt; = 100000,1 <= m&lt; = n-1)。 m是将要发生的战斗次数。

下一行包含n个功率级别。

接下来的m行包含两个整数l和r,表示在第i场战斗中参加骑士阵地的范围。

每次战斗结束后,除了最高功率级别之外的所有夜晚都将被淘汰。

每场战斗的范围是根据骑士的新位置而不是原始位置给出的。

输出:

输出m行,第i行包含该战斗中骑士的原始位置(指数)。每一行都按升序排列。

示例输入:

8 4
1 0 5 6 2 3 7 4
1 3
2 4
1 3
0 1

示例输出:

1 2
4 5
3 7
0

以下是此过程的可视化。

          1     2
[(1,0),(0,1),(5,2),(6,3),(2,4),(3,5),(7,6),(4,7)]
       -----------------
                4     5
[(1,0),(6,3),(2,4),(3,5),(7,6),(4,7)]
             -----------------
          3           7
[(1,0),(6,3),(7,6),(4,7)]
       -----------------
    0
[(1,0),(7,6)]
 -----------

[(7,6)]

我已经解决了这个问题。我的程序产生正确的输出,但是,它是O(n * m)= O(n ^ 2)。我相信如果我从矢量中更有效地消除骑士,效率可以提高。使用集合擦除元素会更有效吗?即擦除连续的段而不是单个骑士。有没有其他方法可以提高效率?

#define INPUT1(x)  scanf("%d", &x)
#define INPUT2(x, y)  scanf("%d%d", &x, &y)
#define OUTPUT1(x) printf("%d\n", x);

int main(int argc, char const *argv[]) {
    int n, m;
    INPUT2(n, m);
    vector< pair<int,int> > knights(n);
    for (int i = 0; i < n; i++) {
        int power;
        INPUT(power);
        knights[i] = make_pair(power, i);
    }
    while(m--) {
        int l, r;
        INPUT2(l, r);
        int max_in_range = knights[l].first;
        for (int i = l+1; i <= r; i++) if (knights[i].first > max_in_range) {
            max_in_range = knights[i].first;
        }
        int offset = l;
        int range = r-l+1;
        while (range--) {
            if (knights[offset].first != max_in_range) {
                OUTPUT1(knights[offset].second));
                knights.erase(knights.begin()+offset);
            }
            else offset++;
        }
        printf("\n");
    }
}

2 个答案:

答案 0 :(得分:2)

好吧,从矢量中删除肯定不会有效。从set或unordered set中删除会更有效(使用迭代器而不是索引)。

然而问题仍然是O(n ^ 2),,因为你有两个嵌套的运行n * m次。

<强> - 编辑 -

我相信我现在明白了这个问题:) 首先让我们计算一下上面代码的复杂性。最糟糕的情况是所有战斗中的最大射程为1(每场战斗两晚)并且战斗不是根据位置进行的。这意味着你有战斗(在这种情况下 m = n-1~ = O(n)

  • 第一个while循环运行 n
  • 每次运行一次,总计 n * 1 = n
  • 第二个while循环每次运行一次,使其再次 n
    • 从向量中删除意味着 n-1 移位,使其 O(n)

因此,随着向量的复杂性,总复杂度为 O(n ^ 2)

首先,你真的不需要内部for循环。将第一个骑士作为范围内的最大值,将其中的其余部分逐一进行比较并删除失败的骑士。

现在,我相信可以使用std :: map在 O(nlogn)中完成。地图的关键是位置,值是骑士的等级。

在继续之前,在地图中查找和删除元素是对数的,迭代是不变的。

最后,您的代码应如下所示:

while(m--) // n times
    strongest = map.find(first_position); // find is log(n) --> n*log(n)

    for (opponent = next of strongest; // this will run 1 times, since every range is 1
         opponent in range;
         opponent = next opponent) // iterating is constant
       // removing from map is log(n) --> n * 1 * log(n)
       if strongest < opponent
           remove strongest, opponent is the new strongest
       else
           remove opponent, (be careful to remove it after iterating to next)

好的,现在上限是 O(2 * nlogn)= O(nlogn)。如果范围增加,则会使上部循环的运行时间减少,但会增加删除操作的数量。我确定上限不会改变,让我们做一个功课来计算:)

答案 1 :(得分:1)

采用treap的解决方案非常简单。

对于每个查询,您需要通过隐式键拆分treap,以获得与.jsp范围对应的子树(需要[l, r]次)。 之后,您可以迭代子树并找到具有最大强度的骑士。之后,您只需要将treap的O(log n)[0, l)部分与此骑士对应的节点合并。

很明显,除了子树遍历和打印之外,解决方案的所有部分都在每个查询的[r + 1, end)时间内工作。但是,每个操作只重新插入一个骑士并从范围中删除其余部分,因此输出的大小(以及子树大小的总和)在O(log n)中是线性的。因此,总时间复杂度为n

我认为你不能用标准的stl容器来解决,因为没有标准的容器支持快速通过索引获取迭代器并删除任意元素。