Terlalu banyak bidak di papan catur

10

Dengan bilangan bulat 2n, temukan jumlah cara yang memungkinkan dimana 2n ^ 2 pion hitam dan 2n ^ 2 pion putih dapat diatur pada papan catur 2n oleh 2n sehingga tidak ada pion yang menyerang pion lain.

  • Gadai hitam hanya bisa menyerang gadai putih, dan sebaliknya.
  • Aturan serangan catur yang biasa diikuti, yaitu, kotak serangan pion putih langsung diagonal di depan, dan kotak serangan pion hitam segera mundur secara diagonal (seperti yang terlihat oleh pengamat kulit putih).
  • Semua rotasi, jumlah pantulan berbeda.

Program yang dapat menampilkan semua konfigurasi yang memungkinkan untuk nilai tertinggi 2n dalam 120 detik akan menang. (Namun, semua program dipersilahkan)

Misalnya, program Alice dapat menangani hingga n = 16 dalam 120 detik sementara Bob dapat menangani hingga n = 20 dalam waktu yang sama. Bob menang.

Platform: Linux 2.7GHz @ 4 CPU

Agnishom Chattopadhyay
sumber
2
Apa format outputnya?
Gagang Pintu
2
Untuk pengujian: adakah orang yang mengetahui angka yang terlibat? Saya menemukan 3 solusi untuk 2x2 dan 28 solusi untuk 4x4
edc65
1
@ edc65, saya membuatnya 3, 30, 410. Saya telah memeriksa 3 dan 30 dengan metode alternatif.
Peter Taylor
1
Saya meminta kode saya menghasilkan beberapa yang pertama: 3, 30, 410, 6148, 96120, 1526700. Meskipun, saya tidak punya cara untuk memeriksa. Adakah yang sama?
cmxu
1
Untuk mengklarifikasi prioritas operator, ketika Anda mengatakan 2n^2pion, apakah itu (2n)^2atau 2(n^2)?
Reto Koradi

Jawaban:

9

Java, n = 87 di mesin saya

Hasil untuk n = 87 adalah

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

}

Ini saat ini menggunakan skema pemrograman dinamis yang mengambil operasi O (n ^ 4) untuk menghitung cara menempatkan ppion pada kotak dengan satu warna 0 <= p <= n^2. Saya pikir ini harus dilakukan dengan lebih efisien.

Lihat hasilnya di sini.

Penjelasan

Dalam solusi yang valid, pion putih paling bawah di setiap kolom harus membentuk garis zig-zag seperti ini:

garis gadai

Yaitu, ketinggian garis dalam kolom c harus +/- 1 dari posisinya di kolom c - 1 . Garis juga dapat menuju ke dua baris imajiner di atas bagian atas papan.

Kita dapat menggunakan pemrograman dinamis untuk menemukan sejumlah cara untuk menarik garis pada pertama c kolom yang mencakup p bidak pada kolom tersebut, berada pada ketinggian h di c th kolom, menggunakan hasil untuk kolom c - 1 , ketinggian h + / - 1 , dan jumlah pion p - h .

feersum
sumber
Bisakah Anda membagikan angka untuk n = 87? Atau setidaknya urutan besarnya? Itu harus menjadi angka yang sangat besar ...
Reto Koradi
Saya sedikit bingung dengan apa yang Anda lakukan di sini, tetapi ini sangat mengesankan!
cmxu
Saya pikir saya mendapatkan sebagian besar penjelasan Anda, kecuali ketika Anda mengatakan "Baris juga dapat menuju ke dua baris imajiner di atas papan"
cmxu
@Changming, itu hanya berarti tidak ada pion di kolom itu.
feersum
@feersum Saya melihat itu lebih masuk akal, saya akan melihat apakah saya dapat bekerja melalui logika dan melihat apakah saya dapat menemukan beberapa cara untuk mengimplementasikannya lebih cepat.
cmxu
5

Jawa

Saat ini, kode saya sangat panjang dan membosankan, saya sedang berusaha membuatnya lebih cepat. Saya menggunakan metode rekursif untuk menemukan nilai-nilai. Ini menghitung 5 pertama dalam 2 atau 3 detik, tetapi kemudian menjadi lebih lambat. Juga, saya belum yakin apakah angkanya tepat, tetapi beberapa yang pertama tampaknya sejalan dengan komentar. Setiap saran dipersilahkan.

Keluaran

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

Penjelasan

Ide dasarnya adalah rekursi. Pada dasarnya Anda mulai dengan papan kosong, papan dengan semua nol. Metode rekursif hanya memeriksa untuk melihat apakah itu dapat menempatkan pion hitam atau putih di posisi berikutnya, jika hanya dapat menempatkan satu warna, ia meletakkannya di sana dan memanggil dirinya sendiri. Jika itu dapat menempatkan kedua warna itu menyebut dirinya dua kali, satu dengan masing-masing warna. Setiap kali itu menyebut dirinya mengurangi kuadrat kiri dan warna yang sesuai tersisa. Ketika telah mengisi seluruh papan, ia mengembalikan hitungan saat ini + 1. Jika ternyata tidak ada cara untuk meletakkan pion hitam atau putih di posisi berikutnya, ia mengembalikan 0, yang berarti itu adalah jalan mati.

Kode

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

Cobalah di sini (Tidak berjalan cukup cepat untuk Ideone sehingga nilai terakhir tidak dicetak, sepertinya solusi saya tidak terlalu bagus!)

cmxu
sumber
Saya setuju hingga 6148, dan saya belum menghasilkan nilai apa pun di luar itu.
Peter Taylor
@PeterTaylor Well Agnishom mengatakan bahwa itu seharusnya 3, 28, 408 jadi saya ragu 6148 benar. Aku ingin tahu apa yang kita berdua lakukan salah?
cmxu
Cukup cepat daripada milikku. +1 bahkan jika saya tidak menyetujui hasil
edc65
Halo! Saya tidak pernah mengatakan 28, 408 Urutan yang benar adalah 3,30,410, ...
Agnishom Chattopadhyay
Anda bilang, @ edc65 memiliki nilai yang benar, dan nilainya 28, 408?
cmxu
4

C ++ dengan pthreads, n = 147 156

Hasil terbaru adalah dari menjalankan kode yang sama seperti sebelumnya pada mesin yang lebih gemuk. Ini sekarang dijalankan pada desktop dengan quad-core i7 (Core i7-4770), yang mencapai n = 156 dalam 120 detik. Hasilnya adalah:

7858103688882482349696225090648142317093426691269441606893544257091315906431773702676266198643058148987365151560865308303083064

Ini menggunakan algoritma pemrograman dinamis. Saya awalnya merenungkan pendekatan di mana hasilnya akan dibangun baris demi baris, tetapi saya tidak pernah bisa menemukan cara untuk memperluas solusi tanpa melacak satu ton negara.

Wawasan utama yang memungkinkan solusi yang cukup efisien adalah:

  • Karena pion di kotak hitam hanya bisa menyerang pion di kotak hitam lainnya, dan hal yang sama berlaku untuk kotak putih, kotak hitam dan putih adalah independen, dan dapat diproses secara terpisah. Dan karena mereka setara, kita hanya perlu memproses satu dari keduanya.
  • Masalahnya menjadi jauh lebih mudah saat memproses papan diagonal dengan diagonal.

Jika Anda melihat satu diagonal dari konfigurasi yang valid, itu selalu terdiri dari urutan pion hitam diikuti oleh urutan pion putih (di mana salah satu urutan juga bisa kosong). Dengan kata lain, masing-masing diagonal dapat sepenuhnya ditandai oleh jumlah pion hitamnya.

Oleh karena itu, status yang dilacak untuk setiap diagonal adalah jumlah konfigurasi gadai yang valid untuk setiap kombinasi:

  • Jumlah pion hitam di baris (atau dengan kata lain, posisi dalam diagonal yang memisahkan pion hitam dari pion putih).
  • Jumlah total pion hitam yang digunakan. Kita perlu melacak semuanya per perhitungan gadai karena kita hanya membutuhkan jumlah pion hitam dan pion putih yang sama di bagian paling akhir. Saat memproses diagonal, hitungannya bisa berbeda, dan pada akhirnya tetap menghasilkan solusi yang valid.

Ketika melangkah dari satu diagonal ke yang berikutnya, ada kendala lain untuk membangun solusi yang valid: Posisi yang memisahkan pion hitam dari pion putih tidak dapat meningkat. Jadi jumlah konfigurasi yang valid dihitung sebagai jumlah dari konfigurasi yang valid dari diagonal sebelumnya untuk posisi yang sama atau lebih besar.

Langkah dasar DP kemudian sangat sederhana. Setiap nilai dalam diagonal hanyalah jumlah nilai dari diagonal sebelumnya. Satu-satunya bagian yang agak menyakitkan adalah menghitung indeks dan rentang loop dengan benar. Karena kita sedang mengerjakan diagonal, panjangnya bertambah selama paruh pertama perhitungan, dan berkurang untuk paruh kedua, yang membuat perhitungan rentang loop lebih rumit. Ada juga beberapa pertimbangan untuk nilai-nilai di batas papan, karena mereka hanya memiliki tetangga diagonal di satu sisi ketika melangkah dari diagonal ke diagonal.

Jumlah memori yang digunakan adalah O (n ^ 3). Saya menyimpan dua salinan data negara, dan ping pong di antara mereka. Saya percaya itu akan mungkin untuk beroperasi dengan satu contoh data negara. Tetapi Anda harus sangat berhati-hati agar tidak ada nilai yang diperbarui sebelum nilai lama dikonsumsi sepenuhnya. Juga, itu tidak akan bekerja dengan baik untuk pemrosesan paralel yang saya perkenalkan.

Kompleksitas runtime adalah ... jumlahnya banyak. Ada 4 loop bersarang dalam algoritma, jadi pada pandangan pertama akan terlihat seperti O (n ^ 4). Tetapi Anda jelas membutuhkan bigints pada ukuran ini, dan angkanya sendiri juga lebih panjang pada ukuran yang lebih besar. Jumlah digit dalam hasil tampaknya meningkat secara proporsional ke n, yang akan membuat semuanya O (n ^ 5). Di sisi lain, saya menemukan beberapa peningkatan kinerja, yang menghindari melalui semua loop penuh.

Jadi sementara ini masih merupakan algoritma yang cukup mahal, ia menjadi lebih jauh dari algoritma yang menyebutkan solusi, yang semuanya eksponensial.

Beberapa catatan implementasi:

  • Meskipun bisa ada hingga 2 * n ^ 2 pion hitam di kotak hitam, saya hanya menghitung angka konfigurasi hingga n ^ 2 pion hitam. Karena ada kesimetrian antara pion hitam dan putih, maka konfigurasi hitung untuk k dan 2 * n ^ 2-k adalah sama.
  • Jumlah solusi dihitung pada akhir dari jumlah konfigurasi pada kotak hitam berdasarkan simetri yang sama. Jumlah total solusi (yang harus memiliki 2 * n ^ 2 pion dari masing-masing warna) adalah jumlah konfigurasi untuk pion k hitam pada satu warna kotak dikalikan dengan jumlah konfigurasi untuk pion hitam 2 * n ^ 2-k pada warna kotak yang lain, disimpulkan semua k.
  • Selain hanya menyimpan jumlah konfigurasi per posisi diagonal dan jumlah pegadaian, saya juga menyimpan rentang jumlah pegadaian yang memiliki konfigurasi per posisi yang valid. Ini memungkinkan mengurangi kisaran loop dalam secara substansial. Tanpa ini, saya menemukan bahwa banyak nol sedang ditambahkan. Saya mendapat peningkatan kinerja yang sangat besar dari ini.
  • Algoritma ini paralel dengan cukup baik, terutama pada ukuran besar. Diagonal harus proses berurutan, sehingga ada penghalang di ujung setiap diagonal. Tetapi posisi dalam diagonal dapat diproses secara paralel.
  • Profiling menunjukkan bahwa bottleneck jelas dalam menambahkan nilai bigint. Saya bermain-main dengan beberapa variasi kode, tetapi tidak terlalu dioptimalkan. Saya menduga bahwa mungkin ada peningkatan yang signifikan dari perakitan inline untuk menggunakan penambahan 64-bit dengan carry.

Kode algoritma utama. THREADSmengontrol jumlah utas yang digunakan, di mana jumlah inti CPU harus menjadi titik awal yang masuk akal:

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

Ini juga membutuhkan kelas bigint yang saya tulis untuk tujuan ini. Perhatikan bahwa ini bukan tujuan umum kelas bigint. Itu hanya cukup untuk mendukung operasi yang digunakan oleh algoritma spesifik ini:

#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
Reto Koradi
sumber
0

Fantom

Berikut ini adalah pos awal yang mengatur kerangka kerja. Saya pikir prosedurnya relatif bagus, tetapi implementasinya saat ini agak menyebalkan. Saya mungkin perlu mencoba untuk meminimalkan jumlah perhitungan yang saya lakukan, dan alih-alih hanya memberikan lebih banyak konstanta.

Strategi

Pada dasarnya, setiap pion putih harus menyerang pion putih lainnya. Jadi saya mulai dengan menempatkan pion putih, menempatkan pion di setiap tempat yang diserang, dan pada dasarnya mengisi papan dengan semua tempat yang harus dituju oleh pion putih. Saya berhenti jika saya sudah menambahkan terlalu banyak pion putih. Jika, pada akhir ini, saya memiliki persis 2n ^ 2 pion, itu solusi. Jika kurang dari itu, tambahkan pion putih lain di suatu tempat, isi semua tempat yang diperlukan, dan hitung lagi. Saya membagi secara rekursif setiap kali isi dengan kurang dari 2n ^ 2 ditemukan, dan menghitung jumlah solusi dengan dan tanpa pion terakhir yang saya tambahkan.

Kode

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
  }

}

Keluaran

Hanya membuatnya menjadi 5 sekarang, tapi saya pikir sebagian besar masalah dalam implementasi.

3
30
410
6148
96120

Uji

Kain
sumber
Itu strategi saya juga, tetapi tampaknya terlalu lambat dibandingkan dengan solusi lain yang diposting di sini.
edc65
@ edc65 Pendekatan yang menyebutkan solusi tidak akan memiliki kesempatan. Jika ada keraguan, angka tipis yang dihasilkan oleh algoritma feersum adalah buktinya. Beberapa jenis algoritma pemrograman dinamis yang menghitung jumlah solusi tanpa menghitungnya adalah cara untuk pergi ke sini.
Reto Koradi