Графики: найти раковину менее чем O (| V |) - или показать, что это невозможно сделать

У меня есть граф с узлами n как матрица смежности.

Возможно ли обнаружить раковину менее чем за O(n)?

Если да, то как? Если нет, как мы это докажем?

Вершина стока - это вершина, которая имеет входящие ребра из других узлов и не имеет выходящих ребер.

Ответ 1

Предположим противное, что существует алгоритм, который запрашивает меньше (n-2)/2 ребер, и пусть противник отвечает на эти запросы произвольно. По принципу Pigeonhole существует (по крайней мере) два узла v, w, которые не являются конечной точкой любого заданного края. Если алгоритм выводит v, то противник делает это неправильно, помещая каждое ребро с приемником w, и аналогично, если алгоритм выдает w.

Ответ 2

Чтение ссылки, предоставленной SPWorley Мне напомнили о дереве турнира algo для нахождения минимального элемента в массиве чисел. node в верхней части дерева является минимальным элементом. Так как алгоритм в ссылке также говорит о конкуренции между двумя узлами (v, w), которые преуспевают в w, если v- > w в противном случае v. Мы можем использовать алгоритм, аналогичный поиску минимального элемента, чтобы найти приемник. Тем не менее, node возвращается независимо от наличия раковины. Хотя, если существует приемник, он всегда возвращается. Следовательно, мы, наконец, должны проверить, что возвращаемый node является приемником.

//pseudo code
//M -> adjacency matrix
int a=0
for(int i=1;i<vertices;++i)
{
  if(M[a,i]) a=i;
}

//check that a is sink by reading out 2V entries from the matrix
return a; //if a is a sink, otherwise -1

Ответ 3

Эта страница отвечает на ваш точный вопрос. Алгоритм линейного времени

   def find-possible-sink(vertices):
     if there only one vertex, return it
     good-vertices := empty-set
     pair vertices into at most n/2 pairs
     add any left-over vertex to good-vertices
     for each pair (v,w):
       if v -> w:
         add w to good-vertices
       else:
         add v to good-vertices
     return find-possible-sink(good-vertices)

   def find-sink(vertices):
     v := find-possible-sink(vertices)
     if v is actually a sink, return it
     return "there is no spoon^H^H^H^Hink"

Ответ 4

В случае общего направленного графа нет, и я не думаю, что ему нужно формальное доказательство. В лучшем случае для обнаружения приемника требуется либо идентифицировать node, либо проверить, что у него нет исходящих ребер, или проверить все остальные node и увидеть, что ни у кого из них нет подключений. На практике вы объединяете два в алгоритме устранения, но нет ярлыка.

Кстати, есть несогласие с определением раковины. Обычно не требуется, чтобы все другие узлы подключались к раковине, потому что у вас может быть несколько приемников. Например, нижняя строка на этой диаграмме - все раковины, а верхняя строка - все источники. Однако он позволяет уменьшить сложность до O (n). См. здесь для некоторого слегка искаженного обсуждения.

Ответ 5

Я работаю над этой проблемой, и я считаю, что это делает:

int graph::containsUniversalSink() {
/****************************************************************
 Returns: Universal Sink, or -1 if it doesn't exist
 Paramters: Expects an adjacency-matrix to exist called matrix
****************************************************************/

//a universal sink is a Vertex with in-degree |V|-1 and out-degree 0
//a vertex in a graph represented as an adjacency-matrix is a universal sink
//if and only if its row is all 0s and its column is all 1s except the [i,i] entry - path to itself (hence out-degree |V|-1)
//for i=0..|V|-1, j=0..|V|-1
//if m[i][j]==0 then j is not universal sink (unless i==j) - column is not all 1s
//if m[i][j]==1 then i is not universal sink - row is not all 0s
int i=0,j=1;
while (i<numVertices && j<numVertices) {
    if (j>i && matrix[i][j]==true) {
        //we found a 1, disqualifying vertex i, and we're not in the last row (j>i) so we move to that row to see if it all 0s
        i=j;
        if (j<numVertices-1) {
            //if the row we're moving to is not the last row
            //we want to start checking from one element after the identity element
            //to avoid the possibility of an infinite loop
            j++;
        }
        continue;
    }
    if (j==numVertices-1 && matrix[i][j]==false) {
        //the last element in a row is a 0
        //thus this is the only possible universal sink
        //we have checked the row for 0s from i+1 (or i if we're in the last row) to numVertices-1 (inclusive)
        //we need to check from 0 to i (inclusive)
        for (j=0; j<i+1; j++) {
            if (matrix[i][j]==true || (matrix[j][i]==false && i!=j)) {
                //this row is not all 0s, or this column is not all 1s so return -1 (false)
                return -1;
            }
        }

        //row is all 0s, but we don't know that column is all 1s
        //because we have only checked the column from 0 to i (inclusive), so if i<numVertices-1
        //there could be a 0 in the column
        //so we need to check from i+1 to numVertices-1 (inclusive)
        for (j=i+1; j<numVertices; j++) {
            if (matrix[j][i]==false) {
                return -1;
            }
        }
        //if we get here we have a universal sink, return it
        return i;
    }
    j++;
}

//if we exit the loop there is no universal sink
return -1;

/********************
 Runtime Analysis
 The while loop will execute at most |V| times: j is incremented by 1 on every iteration
 If we reach the end of a row - this can only happen once - then the first for loop will
 execute i times and the second will execute numVertices-i times, for a combined numVertices iterations
 So we have 2|V| loop executions for a run-time bound of O(|V|)
********************/

}

Ответ 6

Я решил решить это.

Я предполагаю, что массивы инициализируются со всеми 0 (иначе N должно быть заполнено 0) и что M является матрицей смежности для графика. Пусть n - число узлов (n = | V |).

j,i = 1;
N = new int[n]
while (j <= n && i <= n) {
  if (N[i] == 1) {
    i++
  } else if (N[j] == 1) {
    j++;
  } else if (M[i,j] == 1) {
    N[i] = 1
    i++
  } else if (i == j) {
    j++
  } else {
    N[j] = 1
    j++
  }
}
for (z = 1 to n) {
  if (N[z] == 0) {
    return z
  }
}
return NULL

Почему это работает (не формальное доказательство): Любой node с любыми ребрами, идущими от него, не является универсальной раковиной. Таким образом, если M [i, j] равно 1 для любого j, я не может быть поглотителем.

Если M [i, j] равно 0 для любого i, то я не имеет ребра к j и j не может быть универсальным потоком.

A 1 в N [i] означает, что я знаю, что это не приемник, и любой node, который, как я знаю, не является приемником, может быть пропущен как для i, так и для j. Я останавливаюсь, когда либо выдает n.

Таким образом, я продолжаю проверять любые узлы, которые я до сих пор не знаю, это не приемник, пока не останется 1 или 0 возможных раковин.

Таким образом, любой node, который все еще равен 0 в конце цикла, должен быть приемником, и будет только 1 или 0 из них.

Почему это O (n): Это всегда увеличивает либо i, либо j. Он останавливается, когда выдается n. Таким образом, наихудший случай для цикла равен 2n. Работа в цикле постоянна. Последний цикл - наихудший случай n. Следовательно, алгоритм O (3n) = O (n).

Это решение основано на идее проблемы знаменитости, которая является способом рассмотрения одной и той же проблемы.

Ответ 7

Существует так много алгоритмов, которые показывают, как найти универсальный приемник в O (n), но они настолько сложны и не могут быть легко поняты. Я нашел его в Интернете на бумаге, который показывает, как найти универсальный слив в O (n) очень плавно.

1) first create a "SINK" set consisting of all vertices of the graph also 
   create an adjacency list of the graph.
2) now choose first 2 elements of the set.
3) if(A(x,y)==1){
       remove x;             // remove x from "SINK" set.
     }else{
       remove y; }           //remove y from "SINK" set.B

По этому алгоритму вы получите раковину node в вашем SINK, установленном в "n-1". это время O (n).