Как выбрать пары точек на отрезке, чтобы максимизировать сумму значений каждой пары точек? - PullRequest
0 голосов
/ 18 января 2020

Эта проблема может быть решена с помощью динамического c программирования со сложностью O(n^3), но мне интересно, есть ли более эффективные способы сделать это.

Давайте предположим, что у нас есть следующие точки на отрезке длиной 10

точки : [1, 3, 5, 9]

отрезок : [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Для каждой пары точек есть значение, например:

[1, 3]: 2; [1, 5]: 4; [1, 9]: 3; [3, 5]: 1; [3, 9]: 5; [5, 9]: 3

Мы хотим выяснить какова максимальная сумма выбранных пар точек, чтобы каждая пара точек была меньше 5 .

В моем примере ([1, 5] - это хорошо, но [3, 9] - нет), и разные пары не могут перекрываться друг с другом ([[1, 5], [5, 9]] - это хорошо, но [[1, 5], [3, 5]] - нет ).

Ответ на этот вопрос [[1, 5], [5, 9]] с суммой 7.

Я могу использовать динамическое c программирование для решения этой проблемы. Я начинаю с выбора относительной пары ближайших точек и наименее близких до самой дальней пары. Продолжая, я использую матрицу n*n, чтобы сохранить текущие результаты в соответствии с предыдущими. Это динамическое c программирование имеет временную сложность O(n^3).

Мне интересно, есть ли более эффективные способы сделать это.

Ответы [ 2 ]

0 голосов
/ 20 января 2020

Существует программное решение O(R^2) dynamici c (R = диапазон координат). Это можно уменьшить до O(N^2) (N = количество пар) путем сжатия координат.

Давайте назовем L[i][j] вес пары с максимальным весом, начиная с k >= i и заканчивая точно на j. L[i][j] можно вычислить в O(R^2) с помощью рекурсии L[i][j] = max(W[i][j], L[i+1][j]).

Теперь давайте назовем M[j] ответом на вашу проблему с добавленным ограничением последней пары (назовем его Q ) заканчивается ровно на j. Обратите внимание, что если ответ состоит из более чем одной пары, в ответе должна быть другая пара, заканчивающаяся на k < j-1. Если это так, то должно быть, что решение для M[j] такое же, как и для M[k], с добавлением Q в конце (в противном случае мы могли бы увеличить M[j], используя M[k] вместо). Кроме того, Q должно начинаться с координаты i > k (поскольку пары не перекрываются), и оно должно быть максимальным, то есть его вес равен L[k+1][j] (в противном случае мы могли бы увеличить M[j], используя L[k+1][j] вместо Q). Также существует особый случай, когда M[j] состоит из одной пары, и в этом случае нет такого k.

При всем этом мы можем сделать рекурсию M[j] = max(L[0][j], max(M[k] + L[k+1][j], for k in [1, j-2])). Вы можете вычислить M[j] в O(R^2) с помощью приведенной выше рекурсии.

Решением вашей проблемы будет max(M[j], for j in [0, R)). Чтобы понизить его до O(N^2), просто отсортируйте все координаты (в O(N log N)), затем сопоставьте их с координатами в [0, 2*N)O(N) с использованием карты ha sh), так как единственное, что имеет значение к проблеме их порядок.

0 голосов
/ 19 января 2020

tldr, O(n^2)

Вы можете использовать несколько графовых алгоритмов:

Каждая из ваших точек (скажем, p) может быть отображена как два узла (po и * 1008). *): 1o, 1c, 3o, 3c 5o, 5c, 9o, 9c, где o обозначает открытие, а c обозначает закрытие.

Поскольку вы рассматриваете пары

  • , каждый открывающий узел может быть соединен с закрытием узел для стоимости, которую вы определили
  • , каждый закрытый узел может быть подключен к открывающему узлу со стоимостью 0 (чтобы разрешить открытие новой пары)

, например: в вашем случае взвешенные ребра:

// user defined
w(1o, 3c) = 2
w(1o, 5c) = 4
w(3o, 5c) = 1
w(5o, 9c) = 3

// enable connection of pairs
w(1c, 1o) = 0
w(1c, 3o) = 0
w(1c, 5o) = 0
w(1c, 9o) = 0
w(3c, 3o) = 0
w(3c, 5o) = 0
w(3c, 9o) = 0
w(5c, 5o) = 0
w(5c, 9o) = 0

Обратите внимание, что ребро e (x, 9o) бесполезно, поскольку мы не можем создать пару с последней точкой в ​​качестве первого элемента пары ...

К этим взвешенным ребрам мы можем добавить дополнительный закрывающий узел S, который подключен ко всем открывающим узлам

w(S, 1o) = 0
w(S, 3o) = 0
w(S, 5o) = 0
w(S, 9o) = 0

У нас есть DAG (график прямой ациклизации c), к которому мы хотим найти самый длинный путь к любому узлу из источника S.

Алгоритм должен быть в O (E + V) , где E - число ребер и V количество вершин

Что касается топологического упорядочения, точки p по возрастанию значения почти уже определяют его: p_c должно предшествовать p_o, так как мы можем иметь ребро (p_c, p_o)

Ниже код находится в O(n^2), с n количеством точек, так как для соединения пар нам нужно создать ребро от каждого закрывающего узла до следующих открывающих ( около n (n + 1) / 2)

//pseudocode: https://www.geeksforgeeks.org/find-longest-path-directed-acyclic-graph/
function longest(V, E) {
  let max = -9000
  const dist = V.reduce((acc, v)=>(acc[v] = -9000, acc), [])
  dist[V[0]] = 0
  V.forEach(u => {
    E[u].forEach(([v, w]) => {
      if (dist[v] < dist[u] + w) {
        dist[v] = dist[u] + w
        if (dist[v] > max) {
          max = dist[v]
        }
      }
    })
  })
  return max
}
function buildVE () {
  const open = x => x+'o'
  const close = x => x+'c'
  const points = [0].concat([1, 3, 5, 9])
  //close occurs before open because 5c can link to 5o
  const V = [close(0)].concat(points.slice(1).flatMap(x => [close(x), open(x)]))
  const E = {}
  const addEdge = (a, b, w) => {
    E[a] = E[a] || []
    E[a].push([b, w])
  }
  addEdge(open(1), close(3), 2)
  addEdge(open(1), close(5), 4)
  addEdge(open(3), close(5), 1)
  addEdge(open(5), close(9), 3)

  for (let i = 0; i < points.length; ++i) {
    for (let j = i; j < points.length; ++j) {
      addEdge(close(points[i]), open(points[j]), 0)
    }
  }
  const last = points[points.length-1]
  E[open(last)] = []
  E[close(last)] = []
  return {V, E}
}
const {V, E} = buildVE()
console.log(longest(V, E))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...