Занадто багато пішаків на шаховій дошці


10

З огляду на ціле число 2n, знайдіть кількість можливих способів, за якими 2n ^ 2 чорних пішаків та 2n ^ 2 білих пішаків можна розташувати на шаховій дошці 2n на 2n, щоб жоден пішак не атакував іншого.

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

Перемагає програма, яка може вивести всі можливі конфігурації за найвищим значенням 2n за 120 секунд. (Хоча всі програми вітаються)

Наприклад, програма Аліси може працювати з n = 16 протягом 120 секунд, тоді як Боба може працювати з n = 20 за той же час. Перемагає Боб.

Платформа: 2,7 ГГц @ 4 ЦП


2
Який вихідний формат?
Дверна ручка

2
Для тесту: чи має хтось уявлення про числа? Я знайшов 3 рішення для 2x2 та 28 розв’язків для 4x4
edc65

1
@ edc65, я роблю це 3, 30, 410. Я перевірив 3 і 30 альтернативним методом.
Пітер Тейлор

1
У мене був згенерований перший код: 3, 30, 410, 6148, 96120, 1526700. Хоча, я не маю можливості перевірити. Хтось отримує те саме?
cmxu

1
Щоб уточнити пріоритет оператора, коли ви говорите 2n^2пішаків, це (2n)^2чи 2(n^2)?
Рето Коради

Відповіді:


9

Java, n = 87 на моїй машині

Результат при n = 87 дорівнює

62688341832480765224168252369740581641682638216282495398959252035334029997073369148728772291668336432168


import java.math.BigInteger;

public class NonattackingPawns {

    static BigInteger count(int n) {
        BigInteger[][] a0 = new BigInteger[n+1][n*n+1], a1 = new BigInteger[n+1][n*n+1], tm;

        for(int h = 0; h <= n; h++) a0[h][0] = h%2==0? BigInteger.ONE: BigInteger.ZERO;

        for(int c = 1; c <= 2*n; c++) {     
            int minp = 0;
            for(int h = 0; h <= n; h++) {
                java.util.Arrays.fill(a1[h], BigInteger.ZERO);
                if(h>0) minp += c >= 2*h-c%2 ? 2*h - c%2 : c;

                int maxp = Math.min(n*(c-1)+h, n*n);
                for(int p = minp; p <= maxp; p++) {
                    BigInteger sum = a0[h][p-h];

                    if(c%2==1 && h>0) 
                        sum = sum.add(a0[h-1][p-h]);
                    else if(c%2==0 && h<n) 
                        sum = sum.add(a0[h+1][p-h]);

                    a1[h][p] = sum;
                }
            }
            tm=a0; a0=a1; a1=tm;
        }
        BigInteger[] s = new BigInteger[n*n+1];
        for(int p = 0; p <= n*n; p++) {
            BigInteger sum = BigInteger.ZERO;
            for(int h = 0; h <= n; h++) sum = sum.add(a0[h][p]);
            s[p] = sum;

        }

        BigInteger ans = BigInteger.ZERO;
        for(int p = 0; p < n*n; p++) ans = ans.add(s[p].multiply(s[p]));
        return ans.shiftLeft(1).add(s[n*n].multiply(s[n*n]));
    }

    public static void main(String[] args) {
        for(int n = 0;; n++) {
            System.out.println(n + " " + count(n));
        }
    }

}

В даний час використовується динамічна схема програмування, що виконує операції O (n ^ 4) для обчислення способів розміщення pпішаків на квадратах одного кольору для 0 <= p <= n^2. Я думаю, що слід зробити це набагато ефективніше.

Ознайомтесь з результатами тут.

Пояснення

У дійсному рішенні найменші білі пішаки у кожному стовпці повинні утворювати зигзагоподібну лінію таким чином:

лінія пішака

Тобто висота рядка у стовпці c повинна бути +/- 1 від її положення у стовпці c - 1 . Лінія також може виходити на два уявні ряди над вершиною дошки.

Ми можемо використовувати динамічне програмування, щоб знайти кількість способів намалювати лінію на перших стовпцях c, що включає p пішаки на цих стовпцях, знаходиться на висоті h на c стовпці, використовуючи результати для стовпця c - 1 , висоти h + / - 1 , а кількість пішаків p - h .


Чи можете ви поділити число на n = 87? Або хоча б на порядок? Це повинно бути дуже велика кількість ...
Рето Коради

Я трохи розгублений у тому, що ти тут робиш, але це дуже вражає!
cmxu

Я думаю, що я отримую більшість ваших пояснень, за винятком випадків, коли ви говорите "Лінія також може перейти на два уявні ряди над вершиною дошки"
cmxu

@Changming, це означає лише, що в цьому стовпці немає пішаків.
feersum

@feersum Я бачу, що це має більше сенсу, я буду бачити, чи зможу я працювати через логіку і бачу, чи зможу я знайти якийсь спосіб реалізувати її ще швидше.
cmxu

5

Java

Наразі мій код дуже довгий і стомлюючий, я працюю над тим, щоб зробити його швидше. Я використовую рекурсивний метод, щоб знайти значення. Він обчислює перші 5 протягом 2 або 3 секунд, але після цього стає набагато повільніше. Крім того, я ще не впевнений, чи правильні цифри, але, здається, перші декілька відповідають коментарям. Будь-які пропозиції вітаються.

Вихідні дані

2x2:    3
4x4:    30
6x6:    410
8x8:    6148
10x10:  96120

Пояснення

Основна ідея - рекурсія. По суті ви починаєте з порожньої дошки, дошки з усіма нулями. Рекурсивний метод просто перевіряє, чи може він поставити чорну або білу пішаку в наступне положення, якщо він може помістити лише один колір, він кладе його туди і дзвонить собі. Якщо він може нанести обидва кольори, він називає себе двічі, по одному з кожним кольором. Кожен раз, коли він називає себе, він зменшує ліві квадрати та лівий відповідний колір. Після заповнення всієї дошки вона повертає поточну кількість + 1. Якщо виявить, що немає можливості поставити чорну або білу пішаку в наступне положення, вона повертає 0, що означає, що це мертвий шлях.

Код

public class Chess {
    public static void main(String[] args){
        System.out.println(solve(1));
        System.out.println(solve(2));
        System.out.println(solve(3));
        System.out.println(solve(4));
        System.out.println(solve(5));
    }
    static int solve(int n){
        int m =2*n;
        int[][] b = new int[m][m];
        for(int i = 0; i < m; i++){
            for(int j = 0; j < m; j++){
                b[i][j]=0;
            }
        }
        return count(m,m*m,m*m/2,m*m/2,0,b);
    }
    static int count(int n,int sqLeft, int bLeft, int wLeft, int count, int[][] b){
        if(sqLeft == 0){
            /*for(int i = 0; i < n; i++){
                for(int j = 0; j < n; j++){
                    System.out.print(b[i][j]);
                }
                System.out.println();
            }
            System.out.println();*/
            return count+1;
        }
        int x=(sqLeft-1)%n;
        int y=(sqLeft-1)/n;
        if(wLeft==0){
            if(y!=0){
                if ((x==0?true:b[x-1][y-1]!=1)&&(x==n-1?true:b[x+1][y-1]!= 1)) {
                    b[x][y] = 2;
                    return count(n, sqLeft-1, bLeft-1, wLeft, count, b);
                } else {
                    return 0;
                }
            } else {
                b[x][y]=2;
                return count(n,sqLeft-1,bLeft-1,wLeft,count,b);
            }
        } else if(bLeft==0){
            if(y!=n-1){
                if((x==0?true:b[x-1][y+1]!=2)&&(x==n-1?true:b[x+1][y+1]!=2)){
                    b[x][y]=1;
                    return count(n,sqLeft-1,bLeft,wLeft-1,count,b);
                } else {
                    return 0;
                }
            } else {
                b[x][y]=1;
                return count(n,sqLeft-1,bLeft,wLeft-1,count,b);
            }
        } else{
            if(y==0){
                if((x==0?true:b[x-1][y+1]!=2)&&(x==n-1?true:b[x+1][y+1]!=2)){
                    int[][] c=new int[n][n];
                    for(int i = 0; i < n; i++){
                        System.arraycopy(b[i], 0, c[i], 0, n);
                    }
                    b[x][y]=2;
                    c[x][y]=1;
                    return count(n,sqLeft-1,bLeft,wLeft-1,count,c)+count(n,sqLeft-1,bLeft-1,wLeft,count,b);
                } else {
                    b[x][y]=2;
                    return count(n,sqLeft-1,bLeft-1,wLeft,count,b);
                }
            }else if(y==n-1){
                if((x==0?true:b[x-1][y-1]!=1)&&(x==n-1?true:b[x+1][y-1]!=1)){
                    int[][] c=new int[n][n];
                    for(int i = 0; i < n; i++){
                        System.arraycopy(b[i], 0, c[i], 0, n);
                    }
                    b[x][y]=2;
                    c[x][y]=1;
                    return count(n,sqLeft-1,bLeft,wLeft-1,count,c)+count(n,sqLeft-1,bLeft-1,wLeft,count,b);
                } else {
                    b[x][y]=1;
                    return count(n,sqLeft-1,bLeft,wLeft-1,count,b);
                }
            }else{
                if(((x==0?true:b[x-1][y-1]!=1)&&(x==n-1?true:b[x+1][y-1]!=1))&&((x==0?true:b[x-1][y+1]!=2)&&(x==n-1?true:b[x+1][y+1]!=2))){
                    int[][] c=new int[n][n];
                    for(int i = 0; i < n; i++){
                        System.arraycopy(b[i], 0, c[i], 0, n);
                    }
                    b[x][y]=2;
                    c[x][y]=1;
                    return count(n,sqLeft-1,bLeft,wLeft-1,count,c)+count(n,sqLeft-1,bLeft-1,wLeft,count,b);
                } else if ((x==0?true:b[x-1][y-1]!=1)&&(x==n-1?true:b[x+1][y-1]!=1)){
                    b[x][y]=2;
                    return count(n,sqLeft-1,bLeft-1,wLeft,count,b);
                } else if ((x==0?true:b[x-1][y+1]!=2)&&(x==n-1?true:b[x+1][y+1]!=2)){
                    b[x][y]=1;
                    return count(n,sqLeft-1,bLeft,wLeft-1,count,b);
                } else {
                    return 0;
                }
            }
        }
    }
}

Спробуйте тут (Не працює досить швидко для Ideone, тому останнє значення не друкується, схоже, моє рішення не дуже добре!)


Я погоджуюся до 6148, і я ще не створив жодних значень, що перевищують це.
Пітер Тейлор

@PeterTaylor Ну Агніш каже, що це має бути 3, 28, 408, тому я сумніваюся, що 6148 має рацію. Цікаво, що ми обидва робимо неправильно?
cmxu

Досить швидше, ніж у мене. +1, навіть якщо я не згоден з результатами
edc65

Привіт! Я ніколи не казав, що це 28, 408 Правильна послідовність - 3,30410, ...
Агніш Чатопадхей

Ви сказали: @ edc65 мав правильні значення, а його значення - 28, 408?
cmxu

4

C ++ з pthreads, n = 147 156

Останній результат - це запуск того ж коду, що і раніше, на машині, що працює на меді. Тепер це було запущено на робочому столі з чотирьохядерним i7 (Core i7-4770), який отримав n = 156 за 120 секунд. Результат:

7858103688882482349696225090648142317093426691269441606893544257091315906431773702676266198643058148987365151560565922891852481847049321541347582728793175114543840164406674137410614843200

Для цього використовується алгоритм динамічного програмування. Я спочатку розмірковував про підходи, де результат будуватиметься ряд за рядом, але я ніколи не міг придумати спосіб розширити рішення, не відстежуючи тонни стану.

Ключовими висновками, які давали можливість досить ефективного рішення, були:

  • Оскільки пішаки на чорних квадратах можуть атакувати пішаків лише на інших чорних квадратах, а те саме стосується і білих квадратів, чорні та білі квадрати є незалежними та можуть оброблятися окремо. А оскільки вони рівноцінні, нам потрібно лише обробити одне з двох.
  • Проблема стає набагато простішою при обробці дошки по діагоналі.

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

Тому стан, відстежуваний для кожної діагоналі, - це кількість дійсних конфігурацій пішака для кожної комбінації:

  • Кількість чорних пішаків у ряду (або іншими словами, положення у діагоналі, що відокремлює чорні пішаки від білих пішаків).
  • Загальна кількість використаних чорних пішаків. Нам потрібно відстежувати все за кількістю пішаків, тому що нам потрібно лише рівну кількість чорних пішаків та білих пішаків у самому кінці. Під час опрацювання діагоналей, підрахунок може бути різним, і в кінцевому підсумку все-таки випливають правильні рішення.

При переході від однієї діагоналі до наступної існує ще одне обмеження для побудови дійсних рішень: Положення, що відокремлює чорні пішаки від білих пішаків, не може збільшуватися. Отже кількість дійсних конфігурацій обчислюється як сума дійсних конфігурацій попередньої діагоналі для позицій, рівних або більших.

Тоді базовий крок DP дуже простий. Кожне значення діагоналі - це лише сума значень попередньої діагоналі. Єдина дещо болюча частина - це правильно обчислити індекси та діапазони циклів. Оскільки ми працюємо за діагоналями, довжина збільшується протягом першої половини обчислення, а для другої половини зменшується, що робить обчислення діапазону циклу більш громіздким. Існують також деякі міркування щодо значень на межі дошки, оскільки вони мають лише діагональні сусіди з одного боку при переході від діагоналі до діагоналі.

Обсяг використовуваної пам'яті становить O (n ^ 3). Я зберігаю дві копії даних про стан і між ними пінг-понг. Я вважаю, що можна було б оперувати одним екземпляром даних про стан. Але вам доведеться бути дуже обережними, щоб жодні значення не оновлювалися до повного споживання старих значень. Крім того, це не буде добре для паралельної обробки, яку я ввів.

Складність виконання - це многочлен. В алгоритмі є 4 вкладені петлі, тож на перший погляд це виглядатиме як O (n ^ 4). Але вам, очевидно, потрібні великі шрифти при цих розмірах, і самі цифри також збільшуються при більших розмірах. Кількість цифр у результаті, здається, збільшується приблизно пропорційно до n, що зробить всю справу O (n ^ 5). З іншого боку, я знайшов деякі покращення продуктивності, що дозволяє уникнути повного діапазону всіх циклів.

Тому, хоча це все ще досить дорогий алгоритм, він стає набагато далі, ніж алгоритми, що перераховують рішення, які всі експоненціальні.

Деякі зауваження щодо реалізації:

  • Хоча на чорних квадратах може бути до 2 * n ^ 2 чорних пішаків, я обчислюю лише конфігураційні номери до n ^ 2 чорних пішаків. Оскільки між чорними та білими пішаками є симетрія, кількість конфігурацій для k та 2 * n ^ 2-k однакова.
  • Кількість рішень обчислюється в кінці з розрахунку конфігурації на чорні квадрати на основі аналогічної симетрії. Загальна кількість рішень (для яких потрібно мати 2 * n ^ 2 пішаки кожного кольору) - це кількість конфігурацій для k чорних пішаків на один колір квадратів, помножене на кількість конфігурацій для 2 * n ^ 2-k чорних пішаків за іншим кольором квадратів, підсумованим по всіх k.
  • Крім простого зберігання підрахунків конфігурації на діагональну позицію та кількість пішаків, я також зберігаю діапазон підрахунків пішаків, які мають дійсні конфігурації на позицію. Це дозволяє значно скоротити діапазон внутрішньої петлі. Без цього я виявив, що додається багато нулів. Я отримав від цього дуже істотне покращення продуктивності.
  • Алгоритм паралелізується досить добре, особливо при великих розмірах. Діагоналі повинні бути процесами послідовно, тому в кінці кожної діагоналі є бар'єр. Але положення всередині діагоналі можна обробити паралельно.
  • Профілювання показує, що вузьке місце є чітким у додаванні значень bigint. Я пограв з деякими варіантами коду, але він не сильно оптимізований. Я підозрюю, що від вбудованої вбудованої системи могло б бути значно покращено використання 64-бітових доповнень з перенесенням.

Основний алгоритм коду. THREADSконтролює кількість використовуваних потоків, де кількість ядер CPU має бути розумним початковим моментом:

#ifndef THREADS
#define THREADS 2
#endif

#if THREADS > 1
#include <pthread.h>
#endif

#include <vector>
#include <iostream>
#include <sstream>

#include "BigUint.h"

typedef std::vector<BigUint> BigUintVec;
typedef std::vector<int> IntVec;

static int N;
static int NPawn;
static int NPos;

static BigUintVec PawnC[2];
static IntVec PawnMinC[2];
static IntVec PawnMaxC[2];

#if THREADS > 1
static pthread_mutex_t ThreadMutex;
static pthread_cond_t ThreadCond;
static int BarrierCount;
#endif

#if THREADS > 1
static void ThreadBarrier()
{
    pthread_mutex_lock(&ThreadMutex);

    --BarrierCount;
    if (BarrierCount)
    {
        pthread_cond_wait(&ThreadCond, &ThreadMutex);
    }
    else
    {
        pthread_cond_broadcast(&ThreadCond);
        BarrierCount = THREADS;
    }

    pthread_mutex_unlock(&ThreadMutex);
}
#endif

static void* countThread(void* pData)
{
    int* pThreadIdx = static_cast<int*>(pData);
    int threadIdx = *pThreadIdx;

    int prevDiagMin = N - 1;
    int prevDiagMax = N;

    for (int iDiag = 1; iDiag < 2 * N; ++iDiag)
    {
        BigUintVec& rSrcC = PawnC[1 - iDiag % 2];
        BigUintVec& rDstC = PawnC[iDiag % 2];

        IntVec& rSrcMinC = PawnMinC[1 - iDiag % 2];
        IntVec& rDstMinC = PawnMinC[iDiag % 2];

        IntVec& rSrcMaxC = PawnMaxC[1 - iDiag % 2];
        IntVec& rDstMaxC = PawnMaxC[iDiag % 2];

        int diagMin = prevDiagMin;
        int diagMax = prevDiagMax;;
        if (iDiag < N)
        {
            --diagMin;
            ++diagMax;
        }
        else if (iDiag > N)
        {
            ++diagMin;
            --diagMax;
        }

        int iLastPos = diagMax;
        if (prevDiagMax < diagMax)
        {
            iLastPos = prevDiagMax;
        }

        for (int iPos = diagMin + threadIdx; iPos <= iLastPos; iPos += THREADS)
        {
            int nAdd = iPos - diagMin;

            for (int iPawn = nAdd; iPawn < NPawn; ++iPawn)
            {
                rDstC[iPos * NPawn + iPawn] = 0;
            }

            rDstMinC[iPos] = NPawn;
            rDstMaxC[iPos] = -1;

            int iFirstPrevPos = iPos;
            if (!nAdd)
            {
                iFirstPrevPos = prevDiagMin;
            }

            for (int iPrevPos = iFirstPrevPos;
                 iPrevPos <= prevDiagMax; ++iPrevPos)
            {
                int iLastPawn = rSrcMaxC[iPrevPos];
                if (iLastPawn + nAdd >= NPawn)
                {
                    iLastPawn = NPawn - 1 - nAdd;
                }

                if (rSrcMinC[iPrevPos] > iLastPawn)
                {
                    continue;
                }

                if (rSrcMinC[iPrevPos] < rDstMinC[iPos])
                {
                    rDstMinC[iPos] = rSrcMinC[iPrevPos];
                }

                if (iLastPawn > rDstMaxC[iPos])
                {
                    rDstMaxC[iPos] = iLastPawn;
                }

                for (int iPawn = rSrcMinC[iPrevPos];
                     iPawn <= iLastPawn; ++iPawn)
                {
                    rDstC[iPos * NPawn + iPawn + nAdd] += rSrcC[iPrevPos * NPawn + iPawn];
                }
            }

            if (rDstMinC[iPos] <= rDstMaxC[iPos])
            {
                rDstMinC[iPos] += nAdd;
                rDstMaxC[iPos] += nAdd;
            }
        }

        if (threadIdx == THREADS - 1 && diagMax > prevDiagMax)
        {
            int pawnFull = (iDiag + 1) * (iDiag + 1);
            rDstC[diagMax * NPawn + pawnFull] = 1;
            rDstMinC[diagMax] = pawnFull;
            rDstMaxC[diagMax] = pawnFull;
        }

        prevDiagMin = diagMin;
        prevDiagMax = diagMax;

#if THREADS > 1
        ThreadBarrier();
#endif
    }

    return 0;
}

static void countPawns(BigUint& rRes)
{
    NPawn = N * N + 1;
    NPos = 2 * N;

    PawnC[0].resize(NPos * NPawn);
    PawnC[1].resize(NPos * NPawn);

    PawnMinC[0].assign(NPos, NPawn);
    PawnMinC[1].assign(NPos, NPawn);

    PawnMaxC[0].assign(NPos, -1);
    PawnMaxC[1].assign(NPos, -1);

    PawnC[0][(N - 1) * NPawn + 0] = 1;
    PawnMinC[0][N - 1] = 0;
    PawnMaxC[0][N - 1] = 0;

    PawnC[0][N * NPawn + 1] = 1;
    PawnMinC[0][N] = 1;
    PawnMaxC[0][N] = 1;

#if THREADS > 1
    pthread_mutex_init(&ThreadMutex, 0);
    pthread_cond_init(&ThreadCond, 0);

    BarrierCount = THREADS;

    int threadIdxA[THREADS] = {0};
    pthread_t threadA[THREADS] = {0};
    for (int iThread = 0; iThread < THREADS; ++iThread)
    {
        threadIdxA[iThread] = iThread;
        pthread_create(threadA + iThread, 0, countThread, threadIdxA + iThread);
    }

    for (int iThread = 0; iThread < THREADS; ++iThread)
    {
        pthread_join(threadA[iThread], 0);
    }

    pthread_cond_destroy(&ThreadCond);
    pthread_mutex_destroy(&ThreadMutex);
#else
    int threadIdx = 0;
    countThread(&threadIdx);
#endif

    BigUint solCount;
    BigUintVec& rResC = PawnC[1];
    for (int iPawn = 0; iPawn < NPawn; ++iPawn)
    {
        BigUint nComb = rResC[(N - 1) * NPawn + iPawn];

        nComb *= nComb;
        if (iPawn < NPawn - 1)
        {
            nComb *= 2;
        }

        solCount += nComb;
    }

    std::string solStr;
    solCount.toDecString(solStr);
    std::cout << solStr << std::endl;
}

int main(int argc, char* argv[])
{
    std::istringstream strm(argv[1]);
    strm >> N;

    BigUint res;
    countPawns(res);

    return 0;
}

Для цього також потрібен клас bigint, який я написав для цієї мети. Зауважте, що це не клас bigint загального призначення. Цього достатньо для підтримки операцій, використовуваних цим конкретним алгоритмом:

#ifndef BIG_UINT_H
#define BIG_UINT_H

#include <cstdint>
#include <string>
#include <vector>

class BigUint
{
public:
    BigUint()
      : m_size(1),
        m_cap(MIN_CAP),
        m_valA(m_fixedValA)
    {
        m_valA[0] = 0;
    }

    BigUint(uint32_t val)
      : m_size(1),
        m_cap(MIN_CAP),
        m_valA(m_fixedValA)
    {
        m_valA[0] = val;
    }

    BigUint(const BigUint& rhs)
      : m_size(rhs.m_size),
        m_cap(MIN_CAP),
        m_valA(m_fixedValA)
    {
        if (m_size > MIN_CAP)
        {
            m_cap = m_size;
            m_valA = new uint32_t[m_cap];
        }

        for (int iVal = 0; iVal < m_size; ++iVal)
        {
            m_valA[iVal] = rhs.m_valA[iVal];
        }
    }

    ~BigUint()
    {
        if (m_cap > MIN_CAP)
        {
            delete[] m_valA;
        }
    }

    BigUint& operator=(uint32_t val)
    {
        m_size = 1;
        m_valA[0] = val;

        return *this;
    }

    BigUint& operator=(const BigUint& rhs)
    {
        if (rhs.m_size > m_cap)
        {
            if (m_cap > MIN_CAP)
            {
                delete[] m_valA;
            }

            m_cap = rhs.m_size;
            m_valA = new uint32_t[m_cap];
        }

        m_size = rhs.m_size;

        for (int iVal = 0; iVal < m_size; ++iVal)
        {
            m_valA[iVal] = rhs.m_valA[iVal];
        }

        return *this;
    }

    BigUint& operator+=(const BigUint& rhs)
    {
        if (rhs.m_size > m_size)
        {
            resize(rhs.m_size);
        }

        uint64_t sum = 0;
        for (int iVal = 0; iVal < m_size; ++iVal)
        {
            sum += m_valA[iVal];
            if (iVal < rhs.m_size)
            {
                sum += rhs.m_valA[iVal];
            }
            m_valA[iVal] = sum;
            sum >>= 32u;
        }

        if (sum)
        {
            resize(m_size + 1);
            m_valA[m_size - 1] = sum;
        }

        return *this;
    }

    BigUint& operator*=(const BigUint& rhs)
    {
        int resSize = m_size + rhs.m_size - 1;
        uint32_t* resValA = new uint32_t[resSize];

        uint64_t sum = 0;

        for (int iResVal = 0; iResVal < resSize; ++iResVal)
        {
            uint64_t carry = 0;

            for (int iLhsVal = 0;
                 iLhsVal <= iResVal && iLhsVal < m_size; ++iLhsVal)
            {
                int iRhsVal = iResVal - iLhsVal;
                if (iRhsVal < rhs.m_size)
                {
                    uint64_t prod = m_valA[iLhsVal];
                    prod *= rhs.m_valA[iRhsVal];
                    uint64_t newSum = sum + prod;
                    if (newSum < sum)
                    {
                        ++carry;
                    }
                    sum = newSum;
                }
            }

            resValA[iResVal] = sum & UINT64_C(0xFFFFFFFF);
            sum >>= 32u;
            sum += carry << 32u;
        }

        if (resSize > m_cap)
        {
            if (m_cap > MIN_CAP)
            {
                delete[] m_valA;
            }

            m_cap = resSize;
            m_valA = resValA;
        }
        else
        {
            for (int iVal = 0; iVal < resSize; ++iVal)
            {
                m_valA[iVal] = resValA[iVal];
            }

            delete[] resValA;
        }

        m_size = resSize;

        if (sum)
        {
            resize(m_size + 1);
            m_valA[m_size - 1] = sum;
        }

        return *this;
    }

    void divMod(uint32_t rhs, uint32_t& rMod)
    {
        uint64_t div = 0;
        for (int iVal = m_size - 1; iVal >= 0; --iVal)
        {
            div <<= 32u;
            div += m_valA[iVal];

            uint64_t val = div / rhs;
            div -= val * rhs;

            if (val || iVal == 0 || iVal < m_size - 1)
            {
                m_valA[iVal] = val;
            }
            else
            {
                --m_size;
            }
        }

        rMod = div;
    }

    void toDecString(std::string& rStr) const
    {
        std::vector<char> digits;

        BigUint rem(*this);
        while (rem.m_size > 1 || rem.m_valA[0])
        {
            uint32_t digit = 0;
            rem.divMod(10, digit);
            digits.push_back(digit);
        }

        if (digits.empty())
        {
            rStr = "0";
        }
        else
        {
            rStr.clear();
            rStr.reserve(digits.size());

            for (int iDigit = digits.size() - 1; iDigit >= 0; --iDigit)
            {
                rStr.append(1, '0' + digits[iDigit]);
            }
        }
    }

private:
    static const int MIN_CAP = 8;

    void resize(int newSize)
    {
        if (newSize > m_cap)
        {
            uint32_t* newValA = new uint32_t[newSize];

            for (int iVal = 0; iVal < m_size; ++iVal)
            {
                newValA[iVal] = m_valA[iVal];
            }

            if (m_cap > MIN_CAP)
            {
                delete[] m_valA;
            }

            m_cap = newSize;
            m_valA = newValA;
        }

        for (int iVal = m_size; iVal < newSize; ++iVal)
        {
            m_valA[iVal] = 0;
        }

        m_size = newSize;
    }

    int m_size;
    int m_cap;

    uint32_t* m_valA;
    uint32_t m_fixedValA[MIN_CAP];
};

#endif // BIG_UINT_H

0

Fantom

Ось початковий пост, який встановлює рамки. Я вважаю, що процедура є досить хорошою, але реалізація зараз настільки гарна. Мені потрібно, мабуть, спробувати мінімізувати кількість обчислень, які я роблю, а натомість просто пропустити більше констант.

Стратегія

В основному кожен білий пішак повинен атакувати інші білі пішаки. Тож я починаю з розміщення білої пішаки, розміщення пішаків у кожному місці, на яке вона атакує, і по суті заповнення дошки всіма місцями, на які БУДЕ піти піша пішака. Я зупиняюсь, якщо вже додав занадто багато білих пішаків. Якщо наприкінці цього я маю рівно 2n ^ 2 пішаків, це рішення. Якщо менше цього, десь додайте ще одну білу пішачку, заповніть усі необхідні місця та порахуйте ще раз. Я рекурсивно розбиваю щоразу, коли знайдено заливку менше ніж 2n ^ 2, і обчислюю кількість рішень з останньою доданою пішакою і без неї.

Код

class main
{
  public  Void main(){

    echo(calculate(1))
    echo(calculate(2))
    echo(calculate(3))
    echo(calculate(4))
    echo(calculate(5))

  }

  public static  Int calculate(Int n){

    n *= 2
    //Initialize the array -  Definitely a weakpoint, but only runs once
    Bool[][] white := [,]
    n.times{ 
      row := [,]
      n.times{ row.add(false) }
      white.add(row)
    }

    return recurse(white, -1, 0, n, n*n/2)
  }

  private static  Int recurse(Bool[][] white, Int lastPlacement, Int numWhites, Int n, Int totalWhite){
    if(totalWhite - numWhites > n*n - 1 - lastPlacement) return 0
    lastPlacement++
    Int row := lastPlacement / n
    Int col := lastPlacement % n
    if(white[row][col]){ return recurse(white, lastPlacement, numWhites, n, totalWhite)}
    Bool[][] whiteCopy := copy(white)
    whiteCopy[row][col] = true
    Int result := fillIn(whiteCopy, numWhites + 1, totalWhite)
    if(result == -1){
      return recurse(white, lastPlacement, numWhites,n, totalWhite);
    }
    else if(result == totalWhite){
      //echo("Found solution")
      //echo("WhiteCopy = $whiteCopy")
      return recurse(white, lastPlacement, numWhites,n, totalWhite) + 1;
    }
    else return recurse(whiteCopy, lastPlacement, result,n, totalWhite) + recurse(white, lastPlacement, numWhites,n, totalWhite)


  }

  //Every white must be attacking other whites, so fill in the grid with all necessary points
  //Stop if number of whites used goes too high
  private static Int fillIn(Bool[][] white, Int count, Int n){
    white[0..-2].eachWhile |Bool[] row, Int rowIndex -> Bool?| {
      return row.eachWhile |Bool isWhite, Int colIndex -> Bool?|{
        if(isWhite){
          //Catching index out of bounds is faster than checking index every time
          try{
            if(colIndex > 0 && !white[rowIndex + 1][colIndex - 1]){
              white[rowIndex + 1][colIndex - 1] = true
              count++
            }
            if(!white[rowIndex + 1][colIndex + 1]){
              white[rowIndex + 1][colIndex + 1] = true
              count++
            }
          } catch {}
        }
        if(count > n){ count = -1; return true}
        return null
      }//End row.each
    }//End white.each
    return count
  }

  private static Bool[][] copy(Bool[][] orig){
    Bool[][] copy := [,]
    orig.each{
      copy.add(it.dup)
    }
    return copy
  }

}

Вихідні дані

Зараз це лише 5, але я думаю, що більша частина питання знаходиться у виконанні.

3
30
410
6148
96120

Тест


Це теж моя стратегія, але здається, що це занадто повільно порівняно з іншими рішеннями, розміщеними тут.
edc65

@ edc65 Підходи, які перераховують рішення, не матимуть шансів. Якщо виникли якісь сумніви, доказ цього може бути рівним числом, яке виробляється алгоритмом feersum. Сюди можна дійти якийсь алгоритм динамічного програмування, який обчислює кількість рішень без їх перерахування.
Рето Коради
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.