Блог пользователя ilyakrasnovv

Автор ilyakrasnovv, 3 года назад, По-русски

Частный случай — 1-2-BFS

Постановка задачи: дан взвешенный ориентированный граф, где для любого ребра вес лежит в диапазоне $$$[1;2)$$$. Найти кратчайший путь от заданной вершины (st) до всех вершин.

Решение: заведем для каждого целого числа от $$$0$$$ до $$$2n - 1$$$ очередь вершин (Пусть $$$i$$$-ая очередь это $$$q_i$$$). В $$$i$$$-ой очереди будут лежать все вершины, расстояние до которых $$$\lfloor i \rfloor$$$ (и возможно какие-то вершины с меньшим расстоянием). Чтобы такие насчитать, будем пересчитывать по слоям.

код на c++:

for (int i = 0; i < 2 * n; ++i) {
    for (int v : q[i]) {
        for (pair<int, double> e : g[v]) {
            if (dist[e.first] > dist[v] + e.second) { // dist -- расстояния до вершин от стартовой
                dist[e.first] = dist[v] + e.second;
                q[floor(dist[e.first])].push_back(e.first);
            }
        }
    }
}

Доказательство корректности алгоритма:

База: $$$q_0$$$ и $$$q_1$$$ построены правильно.

Предположение: Для $$$x \ge 1$$$ слои $$$q_0, q_1, ..., q_x$$$ построены правильно, и расстояния до вершин в них посчитаны правильно.

Переход: тогда слой $$$q_{x + 1}$$$ тоже построен правильно, и расстояния до вершин в нем правильные:

Посмотрим на любую вершину v, для которой $$$\lfloor dist_v \rfloor = x + 1$$$. Она точно не могла лежать в предыдущих слоях по предположению, а в слое $$$x + 1$$$ она лежит, так как предыдущая в пути от стартовой до нее точно лежала в слое $$$x - 1$$$ или $$$x$$$. А так как расстояния до всех возможных предыдущих посчитаны правильно, то и до новой вершины среди одной из релаксаций будет посчитано правильное расстояние.

Асимптотика: $$$\mathcal{O}(n + m)$$$, где n — число вершин, а m — число ребер.

Комментарий: очевидно, что как и в случае 1-k bfs тут можно обойтись и 3 слоями, но приведенный выше способ лучше для понимания.

Сведение к A-2A-BFS

Отличие этой задачи в том, что в отличие от предыдущей веса ребер находятся в диапазоне $$$[A; 2A)$$$, где $$$A > 0$$$. Свести к предыдущей очень просто — надо всего лишь поделить все веса на A. Важным замечанием является то, что при вычислениях может возникнуть погрешность, и лучше избегать нецелых чисел, если возможно. То есть если задана предельная точность, то стоит домножить A и все веса на обратное число, а вершины добавлять в очередь с номером dist[v] / A.

Ниже реализация на c++ для случая, где все веса целые.

for (int i = 0; i < 2 * n; ++i) {
    for (int v : q[i]) {
        for (pair<int, int> e : g[v]) {
            if (dist[e.first] > dist[v] + e.second) {
                dist[e.first] = dist[v] + e.second;
                q[dist[e.first] / A].push_back(e.first);
            }
        }
    }
}
Помним...
  • Проголосовать: нравится
  • +82
  • Проголосовать: не нравится

»
3 года назад, # |
  Проголосовать: нравится +3 Проголосовать: не нравится

кто из вас видел хотя бы одну задачу на А-2А bfs? скиньте сюда пожалуйста, если видели

»
3 года назад, # |
Rev. 3   Проголосовать: нравится +8 Проголосовать: не нравится

This appears to be a specific case of Dial's algorithm, and in general, you can extend this to edge weights up of size $$$k$$$ in $$$\mathcal O(nk + m)$$$ with $$$k + 1$$$ queues as described here.

  • »
    »
    3 года назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    Also, I'm a little confused on how the $$$A-2A$$$ problem is equivalent to $$$1-2$$$. Is the problem that you have edge weights of length $$$A, A + 1, A + 2, \dots, 2A - 1$$$? Then you say in order to avoid fractional weights, we multiply by some constant first to make them divisible by $$$A$$$, which presumably has to be $$$A$$$ in most cases, so dividing by $$$A$$$ just gives us back our original weights again, so you still have $$$2nA$$$ and not just $$$2n$$$ unique distances?

    • »
      »
      »
      3 года назад, # ^ |
      Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

      The main point of both algorithms is that you can have fractional weight, and that really fractional weights aren't a big problem for 1-k bfs. (But no for 0-k!)

      You can have fractional weights and not only $$$A, A + 1, A + 2, ..., 2A - 1$$$, you can also have weights such as $$$A + 0.5$$$ if it is less than $$$2A$$$, as well as A can be equal to $$$1.333$$$ or $$$10^{18}$$$.

      I multiply them by some number not to make them divisible by A, but to get rid of fractional weights (because we don't want to have such problems as famous $$$0.1 + 0.2$$$). In most cases, I think, multiplying by $$$10^9$$$ will work. During translations I forgot to mention, that we should also multiply $$$A$$$ by this constant.

      • »
        »
        »
        »
        3 года назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится

        Ah, I think I misunderstood what the algorithm was trying to achieve. So to clarify:

        We have fractional weights in the range $$$[1, 2)$$$ (i.e. $$$1.2$$$ and $$$1.555$$$ are permitted). Whenever we transition from a node with shortest distance of, say $$$2.4$$$, then $$$\text{floor}(2.4 + \text{edge weight}) > 2$$$, and in general $$$\text{floor}(\text{old dist} + \text{edge weight}) > \text{floor}(\text{old dist})$$$, putting it in the next queue. And that's why we can use only indices $$$0, 1, 2, \dots, 2(n - 1)$$$, instead of $$$\mathcal O(n \cdot \text{precision})$$$ indices. And that's also why edge weights need to be $$$\geq 1$$$.

        • »
          »
          »
          »
          »
          3 года назад, # ^ |
            Проголосовать: нравится 0 Проголосовать: не нравится

          Yeah, but not always in the next queue, sometimes to queues further. And yes, as you have told, the cool part is that you can store only $$$\mathcal{O}(n)$$$ queues.

»
2 года назад, # |
Rev. 2   Проголосовать: нравится +3 Проголосовать: не нравится

Just found a problem with this in JOI 2016. Problem C. I won't tell you exactly where this is applied here, so as not to spoil it. Task(which I found, at the same time where to submit) $$$ ~ - $$$ https://atcoder.jp/contests/joi2016ho/tasks/joi2016ho_c