Troppe pedine su una scacchiera


10

Dato un numero intero 2n, trova il numero di possibili modi in cui 2n ^ 2 pedine nere e 2n ^ 2 pedine bianche possono essere disposte su una scacchiera 2n da 2n in modo tale che nessuna pedina attacchi un altro.

  • Una pedina nera può attaccare solo una pedina bianca e viceversa.
  • Seguono le solite regole di scacchi dell'attacco, cioè pedine bianche attaccano i quadrati immediatamente in diagonale davanti e le pedine nere attaccano i quadrati immediatamente in diagonale all'indietro (come visto dall'osservatore bianco).
  • Tutta la rotazione, i riflessi sono distinti.

Vince il programma in grado di produrre tutte le possibili configurazioni per il valore più alto di 2n in 120 secondi. (Tutti i programmi sono i benvenuti, però)

Ad esempio, il programma di Alice può gestire fino a n = 16 entro 120 secondi mentre Bob può gestire fino a n = 20 contemporaneamente. Bob vince.

Piattaforma: Linux 2.7GHz @ 4 CPU


2
Qual è il formato di output?
Maniglia della porta

2
Per il test: qualcuno ha idea dei numeri coinvolti? Ho trovato 3 soluzioni per 2x2 e 28 soluzioni per 4x4
edc65 il

1
@ edc65, lo faccio 3, 30, 410. Ho controllato 3 e 30 con un metodo alternativo.
Peter Taylor,

1
Ho avuto il mio codice per generare i primi: 3, 30, 410, 6148, 96120, 1526700. Anche se non ho modo di controllare. Qualcuno ha lo stesso?
cmxu,

1
Per chiarire la precedenza dell'operatore, quando dici 2n^2pedine, è questo (2n)^2o 2(n^2)?
Reto Koradi,

Risposte:


9

Java, n = 87 sulla mia macchina

Il risultato per 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));
        }
    }

}

Attualmente utilizza uno schema di programmazione dinamica che prende le operazioni O (n ^ 4) per calcolare i modi in cui posizionare i ppedoni sui quadrati di un colore 0 <= p <= n^2. Penso che dovrebbe essere possibile farlo in modo molto più efficiente.

Dai un'occhiata ai risultati qui.

Spiegazione

In una soluzione valida, i pedoni bianchi più bassi in ogni colonna devono formare una linea a zigzag in questo modo:

linea di pedone

Cioè, l'altezza della linea nella colonna c deve essere +/- 1 dalla sua posizione nella colonna c - 1 . La linea può anche andare su due file immaginarie sopra la parte superiore della tavola.

Possiamo usare la programmazione dinamica per trovare il numero di modi per disegnare una linea sulle prime colonne c che include pedine p su quelle colonne, è all'altezza h sulla colonna c , usando i risultati per la colonna c - 1 , altezze h + / - 1 e numero di pedine p - h .


Puoi condividere il numero per n = 87? O almeno l'ordine di grandezza? Dev'essere un numero molto grande ...
Reto Koradi,

Sono un po 'confuso su cosa stai facendo qui, ma è davvero impressionante!
cmxu,

Penso di avere la maggior parte delle tue spiegazioni, tranne quando dici "La linea può anche andare su due file immaginarie sopra la parte superiore del tabellone"
cmxu,

@Changming, questo significa solo che non ci sono pedine in quella colonna.
feersum

@feersum Vedo che ha più senso, vedrò se riesco a lavorare attraverso la logica e vedrò se riesco a trovare un modo per implementarlo ancora più velocemente.
cmxu,

5

Giava

Attualmente, il mio codice è molto lungo e noioso, sto lavorando per renderlo più veloce. Uso un metodo ricorsivo per trovare i valori. Calcola i primi 5 entro 2 o 3 secondi, ma in seguito diventa molto più lento. Inoltre, non sono ancora sicuro che i numeri siano corretti, ma i primi sembrano allinearsi ai commenti. Eventuali suggerimenti sono ben accetti

Produzione

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

Spiegazione

L'idea di base è la ricorsione. In sostanza, inizi con una tavola vuota, una tavola con tutti gli zeri. Il metodo ricorsivo controlla solo se può mettere un pedone bianco o nero nella posizione successiva, se può solo mettere un colore, lo mette lì e si chiama da solo. Se riesce a mettere entrambi i colori, si chiama due volte, uno per ogni colore. Ogni volta che si chiama, diminuisce i quadrati a sinistra e il colore appropriato a sinistra. Quando ha riempito l'intera scheda restituisce il conteggio corrente + 1. Se scopre che non c'è modo di mettere una pedina bianca o nera nella posizione successiva, restituisce 0, il che significa che è un percorso morto.

Codice

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

Provalo qui (Non funziona abbastanza velocemente per Ideone, quindi l'ultimo valore non viene stampato, sembra che la mia soluzione non sia molto buona!)


Concordo fino a 6148 e non ho ancora prodotto alcun valore oltre a quello.
Peter Taylor,

@PeterTaylor Bene Agnishom dice che dovrebbe essere 3, 28, 408, quindi dubito che 6148 abbia ragione. Mi chiedo cosa stiamo facendo di sbagliato entrambi?
cmxu,

Abbastanza più veloce del mio. +1 anche se non sono d'accordo sui risultati
edc65,

Ciao! Non ho mai detto che è 28, 408 La sequenza corretta è 3,30,410, ...
Agnishom Chattopadhyay

Hai detto che @ edc65 aveva i valori giusti e i suoi valori sono 28, 408?
cmxu,

4

C ++ con pthreads, n = 147 156

L'ultimo risultato deriva dall'esecuzione dello stesso codice di prima su una macchina più robusta. Questo era ora eseguito su un desktop con un quad-core i7 (Core i7-4770), che arrivava a n = 156 in 120 secondi. Il risultato è:

7858103688882482349696225090648142317093426691269441606893544257091315906431773702676266198643058148987365151560565922891852481847049321541347582728793175114543840164406674137410614843200

Questo sta usando un algoritmo di programmazione dinamica. Inizialmente ho riflettuto su approcci in cui il risultato sarebbe stato costruito riga per riga, ma non sono mai riuscito a trovare un modo per espandere la soluzione senza tracciare una tonnellata di stato.

Le informazioni chiave che hanno consentito una soluzione ragionevolmente efficiente sono state:

  • Poiché i pedoni sui quadrati neri possono attaccare solo i pedoni su altri quadrati neri, e lo stesso vale per i quadrati bianchi, i quadrati bianchi e neri sono indipendenti e possono essere elaborati separatamente. E poiché sono equivalenti, dobbiamo solo elaborare uno dei due.
  • Il problema diventa molto più semplice quando si elabora la tavola in diagonale in diagonale.

Se osservi una diagonale di una configurazione valida, essa consiste sempre in una sequenza di pedine nere seguite da una sequenza di pedine bianche (dove entrambe le sequenze possono anche essere vuote). In altre parole, ogni diagonale può essere completamente caratterizzata dal suo numero di pedine nere.

Pertanto, lo stato monitorato per ciascuna diagonale è il numero di configurazioni di pedone valide per ogni combinazione di:

  • Numero di pedine nere nella riga (o in altre parole, la posizione all'interno della diagonale che separa le pedine nere dalle pedine bianche).
  • Conteggio totale dei pedoni neri utilizzati. Dobbiamo tenere traccia di tutto per numero di pedine perché abbiamo solo bisogno dello stesso numero di pedine nere e pedine bianche alla fine. Durante l'elaborazione delle diagonali, i conteggi possono essere diversi e alla fine risultano comunque valide soluzioni.

Quando si passa da una diagonale alla successiva, esiste un altro vincolo per creare soluzioni valide: la posizione che separa i pedoni neri dai pedoni bianchi non può aumentare. Quindi il numero di configurazioni valide viene calcolato come la somma delle configurazioni valide della diagonale precedente per posizioni uguali o maggiori.

Il passo DP di base è quindi molto semplice. Ogni valore in una diagonale è solo una somma di valori della diagonale precedente. L'unica parte alquanto dolorosa è il calcolo corretto degli indici e degli intervalli di loop. Dato che stiamo lavorando su diagonali, la lunghezza aumenta durante la prima metà del calcolo e diminuisce per la seconda metà, il che rende il calcolo degli intervalli di loop più ingombrante. Ci sono anche alcune considerazioni per i valori al limite del tabellone, poiché hanno solo vicini diagonali su un lato quando si passa dalla diagonale alla diagonale.

La quantità di memoria utilizzata è O (n ^ 3). Conservo due copie dei dati di stato e ping pong tra di loro. Credo che sarebbe possibile operare con un'unica istanza dei dati di stato. Ma dovresti stare molto attento che nessun valore venga aggiornato prima che i vecchi valori siano completamente consumati. Inoltre, non funzionerebbe bene per l'elaborazione parallela che ho introdotto.

La complessità del runtime è ... polinomiale. Ci sono 4 loop nidificati nell'algoritmo, quindi a prima vista sembrerebbe O (n ^ 4). Ma ovviamente hai bisogno di origini in queste dimensioni e anche i numeri si allungano a dimensioni più grandi. Il numero di cifre nel risultato sembra aumentare approssimativamente in modo proporzionale a n, il che renderebbe l'intera cosa O (n ^ 5). D'altra parte, ho trovato alcuni miglioramenti delle prestazioni, che evitano di passare attraverso l'intera gamma di tutti i loop.

Quindi mentre questo è ancora un algoritmo abbastanza costoso, diventa molto più lontano degli algoritmi che enumerano le soluzioni, tutte esponenziali.

Alcune note sull'implementazione:

  • Mentre ci possono essere fino a 2 * n ^ 2 pedine nere sui quadrati neri, io calcolo solo i numeri di configurazione fino a n ^ 2 pedine nere. Poiché esiste una simmetria tra pedine bianche e nere, il conteggio delle configurazioni per k e 2 * n ^ 2-k sono gli stessi.
  • Il numero di soluzioni viene calcolato alla fine dai conteggi di configurazione sui quadrati neri sulla base di una simmetria simile. Il numero totale di soluzioni (che devono avere 2 * n ^ 2 pedine di ogni colore) è il numero di configurazioni per k pedine nere su un colore di quadrati moltiplicato per il numero di configurazioni per pedine nere 2 * n ^ 2-k sull'altro colore dei quadrati, sommato su tutto k.
  • Oltre a memorizzare solo i conteggi di configurazione per posizione diagonale e conteggio dei pegni, memorizzo anche l'intervallo dei conteggi dei pegni con configurazioni valide per posizione. Ciò consente di ridurre sostanzialmente l'intervallo del circuito interno. Senza questo, ho scoperto che venivano aggiunti molti zeri. Ho ottenuto un notevole miglioramento delle prestazioni da questo.
  • L'algoritmo si parallelizza abbastanza bene, in particolare a grandi dimensioni. Le diagonali devono essere processi in sequenza, quindi c'è una barriera alla fine di ogni diagonale. Ma le posizioni all'interno della diagonale possono essere elaborate in parallelo.
  • La profilazione mostra che il collo di bottiglia sta chiaramente nell'aggiungere valori bigint. Ho giocato con alcune varianti del codice, ma non è fortemente ottimizzato. Ho il sospetto che potrebbe esserci un miglioramento significativo dall'assemblaggio in linea per utilizzare aggiunte a 64 bit con carry.

Codice dell'algoritmo principale. THREADScontrolla il numero di thread utilizzati, in cui il numero di core della CPU dovrebbe essere un punto di partenza ragionevole:

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

Anche questo ha bisogno di una lezione bigint che ho scritto per questo scopo. Si noti che questa non è una classe bigint per scopi generici. Fa quanto basta per supportare le operazioni utilizzate da questo algoritmo specifico:

#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

Ecco un post iniziale che configura il framework. Penso che la procedura sia relativamente buona, ma l'implementazione in questo momento fa schifo. Probabilmente ho bisogno di provare a minimizzare il numero di calcoli che sto facendo, e invece passare solo più costanti.

Strategia

Fondamentalmente, ogni pedina bianca deve attaccare altre pedine bianche. Quindi inizio posizionando un pedone bianco, posizionando i pedoni in ogni punto in cui attacca e essenzialmente riempiendo il tabellone con tutti i posti in cui deve andare un pedone bianco. Mi fermo se ho già aggiunto troppe pedine bianche. Se, alla fine, ho esattamente 2n ^ 2 pedine, è una soluzione. Se inferiore a quello, aggiungi un altro pedone bianco da qualche parte, compila tutti i suoi posti richiesti e conta di nuovo. Ho diviso in modo ricorsivo ogni volta che viene trovato un riempimento con meno di 2n ^ 2 e calcolo il numero di soluzioni con e senza l'ultimo pedone che ho aggiunto.

Codice

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
  }

}

Produzione

Adesso arriva solo a 5, ma penso che la maggior parte del problema sia in fase di implementazione.

3
30
410
6148
96120

Test


Anche questa è la mia strategia, ma sembra troppo lenta rispetto alle altre soluzioni pubblicate qui.
edc65,

@ edc65 Gli approcci che elencano le soluzioni non avranno possibilità. In caso di dubbi, il semplice numero prodotto dall'algoritmo di feersum ne è la prova. Una sorta di algoritmo di programmazione dinamica che calcola il numero di soluzioni senza elencarle è la strada da percorrere qui.
Reto Koradi,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.