Оптимизация задачи раскраски индекса с одним соседним цветом разрешена

Я столкнулся с этой проблемой окраски индекса (не совсем типичной проблемой m-раскраски графа), которую я пытался решить с помощью поиска с возвратом, но решение работает должным образом только для тестовых случаев с меньшим значением и терпит неудачу для более крупных из-за экспоненциальной временной сложности.

Как его оптимизировать, чтобы он не выполнялся в экспоненциальное время. Есть ли подход DP для решения этой проблемы?

Постановка задачи:

Вам даны 3 переменные: n, k, x

n -> количество индексов, которые нужно раскрасить

k -> Доступное количество цветов -> 1,2,3,...K

x -> Идентификатор цвета, который можно использовать для окрашивания двух соседних индексов.

Вы должны раскрасить n индексов, расположенных по оси X, в k цветов так, чтобы никакие два соседних индекса не были одного цвета. Однако вам также дается x, который является идентификатором цвета, что является исключением из приведенного выше правила, поэтому разрешено иметь два соседних узла с цветом = идентификатором цвета.

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

Пример. Для n = 3 k = 2 x = 1 : Все возможные решения: (1,1,1), (1,1,2), (1,2,1), (2,1,1), ( 2,1,2)

Ниже приведено мое решение.

public class ColoringIndices {
    public static void main(String[] args) {
        int n = 17;
        int k = 4;
        int x = 3;
        placeColors(n, k, x);
    }

    static int count = 0;

    private static void placeColors(int n, int k, int x) {
        int currpos = 0;
        int[] arr = new int[n];
        placeColorsUtil(n, k, x, currpos, arr);
        System.out.println(count % 1000000007);
        count = 0;
    }

    private static void placeColorsUtil(int n, int k, int x, int currpos, int[] arr) {
        if(currpos == n){
            int mod = 1000000007;
            count = count % mod;
            count++;
            return;
        }

        for (int colorId = 1; colorId <= k; colorId++) {
            if (isSafe(colorId, currpos, arr, x)) {
                arr[currpos] = colorId;
                placeColorsUtil(n, k, x, currpos + 1, arr);
            }
        }
    }

    private static boolean isSafe(int colorId, int currpos, int[] arr, int x) {
        if (currpos < arr.length && currpos > 0) {
            if (colorId != x && arr[currpos - 1] == colorId) {
                return false;
            }
        }
        return true;
    }
}

person Jatin Shashoo    schedule 13.09.2020    source источник
comment
разрешено иметь два... Разрешено ли вам 3 соседних узла особого цвета?   -  person Matt Timmermans    schedule 13.09.2020
comment
@MattTimmermans Решение (1, 1, 1), похоже, указывает на это.   -  person David Eisenstat    schedule 13.09.2020
comment
Ах, верно. Пропустил это.   -  person Matt Timmermans    schedule 13.09.2020


Ответы (1)


Да, DP - это то, был ли последний окрашенный индекс x. Получаем два повтора

X(n) = number of valid sequences of length n ending in x
Y(n) = number of valid sequences of length n not ending in x

X(1) = 1
Y(1) = k-1

X(n+1) = X(n) + Y(n)
         |  |   \__/
          \/     continue sequence not ending in x with x
           continue sequence ending in x with another x

Y(n+1) = (k-1) X(n) + (k-2) Y(n)
         |        |   \________/
         |        |    continue sequence not ending in x in k-2 possible ways
          \______/     (excluding the last color (not x in this case) and x)
           continue sequence ending in x in k-1 possible ways (excl. x)

который можно оценить за время O(n). Ответ: X(n) + Y(n) (или 0, если n равно нулю).

Для потомков я постараюсь получить аналитическое решение. Мех, присутствие k делает это неинтересным. На практике вы просто оцениваете соответствующую мощность матрицы

( 1   1 )
(k-1 k-2)

в любом случае.

person David Eisenstat    schedule 13.09.2020
comment
Это то, что вы имеете в виду? private static void placeColors(int n, int k, int x) { int[][] dp = new int[2][n]; дп[0][0] = 1; дп[1][0] = к - 1; интмод = 1000000007; for (int i = 1; i ‹ n; i++) { dp[0][i] = (dp[0][i - 1] % mod + dp[1][i - 1] % mod) % mod; dp[1][i] = (((k - 1) * dp[0][i - 1]) % mod + ((k - 2) * dp[1][i - 1]) % mod) % мод; } System.out.println(dp[0][n - 1] + dp[1][n - 1]); } - person Jatin Shashoo; 13.09.2020
comment
@JatinShashoo Да (до опечаток), хотя вам не нужно хранить весь массив. - person David Eisenstat; 13.09.2020
comment
Согласованный. Вместо этого можно было бы использовать 2 переменные и обновлять их значения на каждой итерации. Это кажется такой простой проблемой после просмотра вашего объяснения DP. Я не мог подумать об этом раньше в интервью. В то время поиск с возвратом казался единственным интуитивным решением. - person Jatin Shashoo; 13.09.2020