Решить проблему 16-куин за 1 секунду

Я должен решить проблему с 16 ферзями за 1 секунду. Я использовал алгоритм возврата, как показано ниже. Этого кода достаточно для решения проблемы N-Queens за 1 секунду, когда N меньше 13. Но это занимает много времени, если N больше 13.

Как я могу улучшить это?

#include <stdio.h>
#include <stdlib.h>

int n;
int arr[100]={0,};
int solution_count = 0;

int check(int i) 
{ 
    int k=1, ret=1;
    while (k < i && ret == 1) {
        if (arr[i] == arr[k] ||                 
            abs(arr[i]-arr[k]) == abs(i-k))     
            ret = 0; 
        k++;
    }
    return ret;
}

void backtrack(int i) 
{
    if(check(i)) {
        if(i == n) {
            solution_count++;
        } else {
            for(int j=1; j<=n; j++) {
                arr[i+1] = j;
                backtrack(i+1);
            }
        }
    }
}

int main() 
{ 
    scanf("%d", &n);  
    backtrack(0);
    printf("%d", solution_count);
}

2 ответа

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

Во-первых, немного подправьте алгоритм: вместо того, чтобы ждать проверки, пока вы не разместите все N королевы, проверяйте заранее: каждый раз, когда вы собираетесь разместить новую королеву, проверьте, занимает ли другая королева тот же столбец или ту же диагональ, прежде чем arr[i+1] = j; назначение. Это сэкономит вам много циклов процессора.

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

  • У тебя есть N строки
  • У тебя есть N столбцы
  • У тебя есть 2N-1 восходящие диагонали
  • У тебя есть 2N-1 нисходящие диагонали

Поскольку никакие две королевы не могут занимать одно и то же место в любом из четырех "измерений" выше, вам нужен массив логических значений для последних трех вещей; строки гарантированно будут разными, потому что i параметр backtrack, который представляет строку, гарантированно будет другим.

С N до 16, 2N-1 идет до 31, так что вы можете использовать uint32_t для ваших битовых массивов. Теперь вы можете проверить, если столбец c берется путем применения побитового и & к битовой маске столбцов и 1 << c, То же самое касается диагональных битовых масок.

Примечание. Выполнение задачи с 16-ю королевами за секунду было бы довольно сложно. Очень оптимизированная программа делает это за 23 секунды на ПК с частотой 800 МГц. Частота 3,2 ГГц должна дать вам ускорение примерно в 4 раза, но для решения проблемы потребуется около 8 секунд.

Я бы поменял while (k < i && ret == 1) { в while (k < i) {
и вместо ret = 0; делать return 0;,

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

Другие вопросы по тегам