Головоломка с алгоритмом: минимальная стоимость, позволяющая всем людям, стоящим в очереди, общаться друг с другом

У меня есть головоломка по разработке алгоритма, которую я не могу решить.

Загадка формулируется так: На числовой прямой стоят N человек, каждый из которых может стоять на любом целом числе на этой прямой. На один и тот же номер могут стоять несколько человек. Чтобы любые два человека могли общаться друг с другом, расстояние между ними должно быть меньше K. Цель состоит в том, чтобы переместить их так, чтобы каждая пара из двух людей могла общаться друг с другом (возможно, через других людей). Другими словами, нам нужно переместить их так, чтобы расстояние между любыми двумя соседними людьми было меньше, чем K.

Вопрос: Каково минимальное количество ходов? Такое ощущение, что это относится к семейству жадных алгоритмов или динамическому программированию. Любые подсказки приветствуются!


person TomatEgg    schedule 10.10.2015    source источник
comment
Что представляет собой ход? Можем ли мы переместить человека по числовой прямой куда-нибудь еще за один ход?   -  person Rahul Nori    schedule 10.10.2015
comment
Вы можете переместить человека куда угодно, изменение расстояния, вызванное этим перемещением, является ценой. И мы хотим свести к минимуму суммирование всех таких затрат   -  person TomatEgg    schedule 10.10.2015
comment
Не попробовав, я думаю, что алгоритм A * мог бы работать достаточно хорошо: en.wikipedia.org/wiki/A*_search_algorithm Существует хорошая эвристика минимального количества ходов, необходимых для получения решения. Вы только посмотрите, как часто и насколько нарушаются ограничения между соседними людьми.   -  person Henry    schedule 10.10.2015
comment
@ Генри Алгоритм звезды кажется более подходящим для поиска пути, я не совсем понимаю, как его можно использовать для этой проблемы.   -  person TomatEgg    schedule 11.10.2015
comment
Да, считайте каждую конфигурацию узлом, а перемещения — ребрами между узлами. Начните с узла, соответствующего исходной конфигурации, и найдите кратчайший путь к любой разрешенной конфигурации.   -  person Henry    schedule 11.10.2015


Ответы (2)


Мы можем сделать следующее в O(n):

Рассчитайте стоимость перемещения всех людей справа от человека i по направлению к человеку i на приемлемое расстояние:

costRight(A[i]) = costRight(A[i+1]) + (A[i+1] - A[i] - k + 1) * count of people to the right

K = 3;  A = { 0,  3, 11, 17, 21}
costRight = {32, 28, 10,  2,  0}

Рассчитайте стоимость перемещения всех людей слева от человека i по направлению к человеку i на приемлемое расстояние:

costLeft(A[i]) = costLeft(A[i-1]) + (A[i] - A[i-1] - k + 1) * count of people to the left

K = 3;  A = { 0,  3, 11, 17, 21}
costLeft  = { 0,  1, 13, 25, 33}
costRight = {32, 28, 10,  2,  0}

Теперь, когда у нас есть стоимость в обоих направлениях, мы можем сделать это в O(n):

minCost = min(costRight + costLeft) for all A[i]
minCost = min(32 + 0, 28 + 1, 13 + 10, 25 + 2, 33 + 0) = 23

Но иногда этого недостаточно:

K = 3;  A = { 0,  0,  1,  8,  8}

      carry:     -2  -4       3
costLeft  = { 0,  0,  0, 11, 11}

      carry: -3   5      -2
costRight = { 8,  8,  8,  0,  0}

Оптимальным является ни 11, ни 8. Протестируйте текущее лучшее, двигаясь к наибольшей экономии:

move 1 to 2, cost = 1

K = 3;  A = { 0,  0,  2,  8,  8}

      carry:     -2  -2     -10
costLeft  = { 0,  0,  0, 10, 10}

      carry: -2          -2
costRight = { 6,  6,  6,  0,  0}

minCost = 1 + min(0 + 6, 0 + 6, 0 + 6, 10 + 0, 10 + 0) = 1 + 6 = 7

Не совсем уверен, как сформулировать это эффективно.

person גלעד ברקן    schedule 12.10.2015

Вот жадный алгоритм, написанный на Java, но я не знаю, дает ли он оптимальное решение в каждом случае. Также это скорее доказательство концепции, есть место для оптимизации.

Он основан на том факте, что два соседних человека не должны находиться друг от друга более чем на К, следующий сосед не должен быть дальше, чем на 2К, и так далее. На каждом этапе мы перемещаем человека, который «больше всего нарушает эти ограничения». Детали этого расчета находятся в методе calcForce.

package so;

import java.util.Arrays;

public class Main {

    public static void main(String args[]) {
        int[] position = new int[] {0, 0, 5, 11, 17, 23};
        int k = 5;

        solve(position, k);
    }

    private static void solve(int[] position, int k) {
        if (!sorted(position)) {
            throw new IllegalArgumentException("positions must be sorted");
        }
        int[] force = new int[position.length];
        int steps = 0;
        while (calcForce(position, k, force)) {
            int mp = -1;
            int mv = -1;
            for (int i = 0; i < force.length; i++) {
                if (mv < Math.abs(force[i])) {
                    mv = Math.abs(force[i]);
                    mp = i;
                }
            }
            System.out.printf("move %d to the %s%n", mp, force[mp] > 0 ? "right" : "left");
            if (force[mp] > 0) {
                position[mp]++;
            } else {
                position[mp]--;
            }
            steps++;
        }
        System.out.printf("total: %d steps%n", steps);
    }

    private static boolean calcForce(int[] position, int k, int[] force) {
        boolean commProblem = false;
        Arrays.fill(force, 0);
        for (int i = 0; i < position.length - 1; i++) {
            for (int j = i + 1; j < position.length; j++) {
                int f = position[j] - position[i] - (j - i) * k;
                if (f > 0) {
                    force[i] += f;
                    force[j] -= f;
                    commProblem = true;
                }
            }
        }
        return commProblem;
    }

    private static boolean sorted(int[] position) {
        for (int i = 0; i < position.length - 1; i++) {
            if (position[i] > position[i+1]) {
                return false;
            }
        }
        return true;
    }
}
person Henry    schedule 10.10.2015