Muitos peões em um tabuleiro de xadrez


10

Dado um número inteiro 2n, encontre o número possível de maneiras pelas quais 2n ^ 2 peões pretos e 2n ^ 2 peões brancos podem ser organizados em um tabuleiro de xadrez 2n por 2n, de modo que nenhum peão ataque outro.

  • Um peão preto pode atacar apenas um peão branco e vice-versa.
  • Seguem as regras usuais de ataque de xadrez, ou seja, peões brancos atacam quadrados imediatamente na diagonal na frente e os peões pretos atacam quadrados imediatamente na diagonal para trás (como visto pelo observador branco).
  • Toda rotação, reflexões contam como distintas.

O programa que pode gerar todas as configurações possíveis para o valor mais alto de 2n em 120 segundos vence. (Todos os programas são bem-vindos, no entanto)

Por exemplo, o programa de Alice pode lidar com até n = 16 em 120 segundos, enquanto o de Bob pode lidar com até n = 20 no mesmo tempo. Bob vence.

Plataforma: Linux 2.7GHz @ 4 CPUs


2
Qual é o formato de saída?
Maçaneta

2
Para teste: alguém tem alguma idéia dos números envolvidos? Eu encontrei 3 solução para 2x2 e 28 soluções para 4x4
edc65

11
@ edc65, faço 3, 30, 410. Eu verifiquei os 3 e 30 por um método alternativo.
Peter Taylor

11
Meu código gerou os primeiros: 3, 30, 410, 6148, 96120, 1526700. Embora não tenha como verificar. Alguém conseguiu o mesmo?
cmxu

11
Para esclarecer a precedência do operador, quando você diz 2n^2peões, é isso (2n)^2ou 2(n^2)?
Reto Koradi

Respostas:


9

Java, n = 87 na minha máquina

O resultado para 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));
        }
    }

}

Atualmente, ele usa um esquema de programação dinâmica que executa operações O (n ^ 4) para calcular as maneiras de colocar ppeões nos quadrados de uma cor 0 <= p <= n^2. Eu acho que deveria ser possível fazer isso com muito mais eficiência.

Confira os resultados aqui.

Explicação

Em uma solução válida, os peões brancos mais baixos em cada coluna devem formar uma linha em zigue-zague assim:

linha de penhor

Ou seja, a altura da linha na coluna c deve ser +/- 1 em relação à sua posição na coluna c - 1 . A linha também pode ir para duas linhas imaginárias acima da parte superior do quadro.

Podemos usar programação dinâmica para encontrar o número de maneiras para desenhar uma linha sobre os primeiros c colunas que inclui p peões sobre as colunas, é a altura h na c th coluna, utilizando os resultados para a coluna c - 1 , alturas h + / - 1 e número de peões p - h .


Você pode compartilhar o número de n = 87? Ou pelo menos a ordem de magnitude? Isso tem que ser um número muito grande ...
Reto Koradi

Estou um pouco confuso sobre o que você está fazendo aqui, mas é muito impressionante!
cmxu

Eu acho que eu recebo a maioria de sua explicação, a não ser quando você diz "A linha também pode ir para duas linhas imaginárias acima da parte superior da placa"
cmxu

@Changming, isso significa apenas que não há peões nessa coluna.
feersum

@feersum Vejo que faz mais sentido, vou ver se consigo trabalhar com a lógica e ver se consigo encontrar uma maneira de implementá-la ainda mais rapidamente.
Cmxu

5

Java

Atualmente, meu código é muito longo e tedioso, estou trabalhando para torná-lo mais rápido. Eu uso um método recursivo para encontrar os valores. Ele calcula os primeiros 5 em 2 ou 3 segundos, mas fica muito mais lento depois. Além disso, ainda não tenho certeza se os números estão corretos, mas os primeiros parecem se alinhar com os comentários. Todas as sugestões são bem-vindas.

Resultado

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

Explicação

A idéia básica é recursão. Basicamente, você começa com um tabuleiro vazio, um tabuleiro com todos os zeros. O método recursivo apenas verifica se é possível colocar um peão preto ou branco na próxima posição; se pode colocar apenas uma cor, coloca-o lá e chama a si mesmo. Se ele pode colocar as duas cores, chama a si mesmo duas vezes, uma com cada cor. Cada vez que se chama, diminui os quadrados à esquerda e a cor apropriada à esquerda. Quando preenche todo o tabuleiro, retorna a contagem atual + 1. Se descobrir que não há como colocar um peão preto ou branco na próxima posição, ele retornará 0, o que significa que é um caminho morto.

Código

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;
                }
            }
        }
    }
}

Experimente aqui (não roda rápido o suficiente para o Ideone, para que o último valor não seja impresso, parece que minha solução não é muito boa!)


Concordo até 6148 e ainda não produzi valores além disso.
Peter Taylor

@ PeterTaylor Bem, Agnishom diz que deveria ser 3, 28, 408, então duvido que 6148 esteja certo. Eu me pergunto o que nós dois estamos fazendo de errado?
Cmdu #

Muito mais rápido que o meu. +1 mesmo se eu não concordar em resultados
edc65

Olá! Eu nunca disse que é 28, 408 A seqüência correta é 3,30,410, ...
Agnishom Chattopadhyay

Você disse que @ edc65 tinha os valores certos e os valores dele são 28, 408?
Cmdu

4

C ++ com pthreads, n = 147 156

O resultado mais recente é a execução do mesmo código de antes em uma máquina mais robusta. Agora, ele era executado em um desktop com um i7 quad-core (Core i7-4770), que chegava a n = 156 em 120 segundos. O resultado é:

785810368888248234969622509064814231709342669126944160689354425709131590643177370267626619864305814898736515156056592289185580814414414445432440440474340474340474310345448410448474674626448426426426426426460810

Isso está usando um algoritmo de programação dinâmica. Inicialmente, considerei abordagens nas quais o resultado seria construído linha por linha, mas nunca consegui encontrar uma maneira de expandir a solução sem rastrear uma tonelada de estado.

Os principais insights que permitiram uma solução razoavelmente eficiente foram:

  • Como os peões em quadrados pretos só podem atacar peões em outros quadrados pretos, e o mesmo vale para quadrados brancos, os quadrados preto e branco são independentes e podem ser processados ​​separadamente. E, como são equivalentes, precisamos apenas processar um dos dois.
  • O problema fica muito mais fácil ao processar a placa diagonal por diagonal.

Se você observar uma diagonal de uma configuração válida, ela sempre consistirá em uma sequência de peões pretos, seguida por uma sequência de peões brancos (onde qualquer sequência também pode estar vazia). Em outras palavras, cada diagonal pode ser totalmente caracterizada pelo número de peões pretos.

Portanto, o estado rastreado para cada diagonal é o número de configurações de peões válidas para cada combinação de:

  • Número de peões pretos na linha (ou seja, a posição na diagonal que separa os peões pretos dos peões brancos).
  • Contagem total de peões pretos usados. Precisamos rastrear tudo por contagem de peões, porque precisamos apenas do mesmo número de peões pretos e peões brancos no final. Ao processar as diagonais, as contagens podem ser diferentes e ainda resultar em soluções válidas no final.

Ao passar de uma diagonal para a próxima, há outra restrição para criar soluções válidas: A posição que separa os peões pretos dos peões brancos não pode aumentar. Portanto, o número de configurações válidas é calculado como a soma das configurações válidas da diagonal anterior para posições iguais ou maiores.

A etapa básica do DP é então muito simples. Cada valor em uma diagonal é apenas uma soma dos valores da diagonal anterior. A única parte um pouco dolorosa é calcular os índices e as faixas de loop corretamente. Como estamos trabalhando nas diagonais, o comprimento aumenta durante a primeira metade do cálculo e diminui na segunda metade, o que torna o cálculo do intervalo do loop mais complicado. Há também algumas considerações para os valores no limite do quadro, pois eles só têm vizinhos diagonais de um lado ao passar de diagonal para diagonal.

A quantidade de memória usada é O (n ^ 3). Eu mantenho duas cópias dos dados do estado e pingue-pongue entre eles. Eu acredito que seria possível operar com uma única instância dos dados do estado. Mas você deve ter muito cuidado para que nenhum valor seja atualizado antes que os valores antigos sejam totalmente consumidos. Além disso, não funcionaria bem para o processamento paralelo que apresentei.

A complexidade do tempo de execução é ... polinomial. Existem 4 loops aninhados no algoritmo, portanto, à primeira vista, ele se pareceria com O (n ^ 4). Mas você obviamente precisa de bigints nesses tamanhos, e os próprios números também ficam mais longos em tamanhos maiores. O número de dígitos no resultado parece aumentar proporcionalmente a n, o que tornaria a coisa toda O (n ^ 5). Por outro lado, encontrei algumas melhorias de desempenho, o que evita passar por toda a gama de todos os loops.

Portanto, embora esse algoritmo ainda seja bastante caro, ele vai muito além dos algoritmos que enumeram soluções, todas exponenciais.

Algumas notas sobre a implementação:

  • Embora possa haver até 2 * n ^ 2 peões pretos nos quadrados pretos, só calculo os números de configuração até n ^ 2 peões pretos. Como existe uma simetria entre os peões preto e branco, a contagem de configurações de ke 2 * n ^ 2-k é a mesma.
  • O número de soluções é calculado no final das contagens de configuração nos quadrados pretos com base em uma simetria semelhante. O número total de soluções (que precisam ter 2 * n ^ 2 peões de cada cor) é o número de configurações para k peões pretos em uma cor de quadrados multiplicado pelo número de configurações para 2 * n ^ 2-k peões pretos na outra cor dos quadrados, somados todos os k.
  • Além de armazenar apenas as contagens de configuração por posição diagonal e contagem de peões, também armazeno o intervalo de contagens de peões que possuem configurações válidas por posição. Isso permite reduzir substancialmente o alcance do circuito interno. Sem isso, descobri que muitos zeros estavam sendo adicionados. Eu obtive uma melhoria de desempenho muito substancial com isso.
  • O algoritmo é paralelo bastante bem, principalmente em tamanhos grandes. As diagonais devem ser processadas seqüencialmente, para que haja uma barreira no final de cada diagonal. Mas as posições dentro da diagonal podem ser processadas em paralelo.
  • A criação de perfil mostra que o gargalo está claramente na adição de valores grandes. Eu brinquei com algumas variações do código, mas não é muito otimizado. Suspeito que possa haver uma melhoria significativa do assembly embutido para usar adições de 64 bits com carry.

Código do algoritmo principal. THREADScontrola o número de threads usados, em que o número de núcleos da CPU deve ser um ponto de partida razoável:

#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;
}

Isso também precisa de uma grande aula que escrevi para esse fim. Observe que essa não é uma classe bigint de uso geral. Ele faz o suficiente para suportar as operações usadas por este algoritmo específico:

#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

Aqui está um post inicial que configura a estrutura. Eu acho que o procedimento é relativamente bom, mas a implementação no momento é meio ruim. Preciso provavelmente tentar minimizar o número de cálculos que estou fazendo e, em vez disso, passar mais constantes.

Estratégia

Basicamente, cada peão branco deve estar atacando outros peões brancos. Então eu começo colocando um peão branco, colocando peões em cada lugar que ele ataca, e essencialmente preenchendo o tabuleiro com todos os lugares que um peão branco tem que ir. Paro se já adicionei muitos peões brancos. Se, no final, tiver exatamente 2n ^ 2 peões, é uma solução. Se for menor do que isso, adicione outro peão branco em algum lugar, preencha todos os lugares necessários e conte novamente. Eu divido recursivamente toda vez que um preenchimento com menos de 2n ^ 2 é encontrado e calculo o número de soluções com e sem o último peão que adicionei.

Código

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
  }

}

Resultado

Agora chega a 5 agora, mas acho que a maior parte do problema está em implementação.

3
30
410
6148
96120

Teste


Essa também é minha estratégia, mas parece muito lenta em comparação com as outras soluções postadas aqui.
Edc65

@ edc65 As abordagens que enumeram as soluções não terão chance. Se houver alguma dúvida, o grande número produzido pelo algoritmo de feersum é a prova disso. Algum tipo de algoritmo de programação dinâmica que calcula o número de soluções sem enumerá-las é o caminho a seguir.
Reto Koradi
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.