Quá nhiều quân cờ trên bàn cờ vua


10

Cho một số nguyên 2n, tìm số cách có thể trong đó 2n ^ 2 pawns đen và 2n ^ 2 pawns trắng có thể được sắp xếp trên bàn cờ 2n 2n để không có con tốt nào tấn công người khác.

  • Một con tốt màu đen chỉ có thể tấn công một con tốt trắng và ngược lại.
  • Các quy tắc cờ thông thường của tấn công theo sau, tức là những con cờ trắng tấn công các ô vuông ngay lập tức theo đường chéo ở phía trước, và những con cờ đen tấn công các ô vuông ngay lập tức theo đường chéo (theo quan sát của người quan sát trắng).
  • Tất cả các vòng quay, phản xạ được tính là khác biệt.

Chương trình có thể xuất tất cả các cấu hình có thể có giá trị cao nhất là 2n trong 120 giây sẽ thắng. (Tất cả các chương trình đều được chào đón)

Ví dụ: chương trình của Alice có thể xử lý tối đa n = 16 trong vòng 120 giây trong khi Bob có thể xử lý tối đa n = 20 trong cùng một lúc. Bob thắng.

Nền tảng: CPU Linux 2.7GHz @ 4


2
Định dạng đầu ra là gì?
Doorknob

2
Để kiểm tra: có ai có ý tưởng nào về những con số liên quan không? Tôi đã tìm thấy 3 giải pháp cho 2x2 và 28 giải pháp cho 4
x

1
@ edc65, tôi làm cho nó 3, 30, 410. Tôi đã kiểm tra 3 và 30 bằng một phương pháp thay thế.
Peter Taylor

1
Tôi đã tạo mã của mình trong vài lần đầu tiên: 3, 30, 410, 6148, 96120, 1526700. Mặc dù, tôi không có cách nào để kiểm tra. Bất cứ ai cũng nhận được như vậy?
cmxu

1
Để làm rõ về quyền ưu tiên của nhà điều hành, khi bạn nói 2n^2cầm đồ, đó là (2n)^2hay 2(n^2)?
Reto Koradi

Câu trả lời:


9

Java, n = 87 trên máy của tôi

Kết quả cho n = 87 là

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

}

Điều này hiện đang sử dụng sơ đồ lập trình động thực hiện các thao tác O (n ^ 4) để tính toán các cách đặt các pcon tốt trên các ô vuông của một màu cho 0 <= p <= n^2. Tôi nghĩ rằng nó có thể làm điều này hiệu quả hơn nhiều.

Kiểm tra kết quả tại đây.

Giải trình

Trong một giải pháp hợp lệ, những con tốt trắng thấp nhất trong mỗi cột phải tạo thành một đường ngoằn ngoèo như thế này:

dòng cầm đồ

Nghĩa là, chiều cao của dòng trong cột c phải là +/- 1 từ vị trí của nó trong cột c - 1 . Dòng này cũng có thể đi vào hai hàng tưởng tượng phía trên đầu bảng.

Chúng ta có thể sử dụng lập trình động để tìm số cách vẽ một dòng trên các cột c đầu tiên bao gồm p pawns trên các cột đó, ở độ cao h trên cột c , sử dụng kết quả cho cột c - 1 , độ cao h + / - 1 và số lượng con tốt p - h .


Bạn có thể chia sẻ số cho n = 87 không? Hoặc ít nhất là thứ tự của độ lớn? Đó phải là một con số rất lớn ...
Reto Koradi

Tôi hơi bối rối về những gì bạn đang làm ở đây, nhưng nó rất ấn tượng!
cmxu

Tôi nghĩ rằng tôi nhận được hầu hết lời giải thích của bạn, ngoại trừ khi bạn nói "Dòng này cũng có thể đi vào hai hàng tưởng tượng phía trên đầu bảng"
cmxu

@ Thay đổi, điều đó chỉ có nghĩa là không có con tốt trong cột đó.
frageum

@feersum Tôi thấy điều đó có ý nghĩa hơn, tôi sẽ xem liệu tôi có thể làm việc thông qua logic hay không và xem liệu tôi có thể tìm ra cách nào đó để thực hiện nó nhanh hơn không.
cmxu

5

Java

Hiện tại, mã của tôi rất dài và tẻ nhạt, tôi đang làm việc để làm cho nó nhanh hơn. Tôi sử dụng một phương thức đệ quy để tìm các giá trị. Nó tính toán 5 đầu tiên trong vòng 2 hoặc 3 giây, nhưng sau đó sẽ chậm hơn nhiều. Ngoài ra, tôi không chắc chắn nếu các con số là đúng, nhưng vài số đầu tiên dường như xếp hàng với các bình luận. Mọi góp ý đều được chào đón.

Đầu ra

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

Giải trình

Ý tưởng cơ bản là đệ quy. Về cơ bản, bạn bắt đầu với một bảng trống, một bảng với tất cả các số không. Phương thức đệ quy chỉ kiểm tra xem liệu nó có thể đặt một con tốt đen hoặc trắng ở vị trí tiếp theo hay không, nếu nó chỉ có thể đặt một màu, nó sẽ đặt nó ở đó và tự gọi nó. Nếu nó có thể đặt cả hai màu, nó tự gọi hai lần, một màu với mỗi màu. Mỗi lần nó tự gọi nó sẽ giảm các hình vuông bên trái và màu sắc phù hợp còn lại. Khi nó đã lấp đầy toàn bộ bảng, nó trả về số hiện tại + 1. Nếu phát hiện ra rằng không có cách nào để đặt một con tốt đen hoặc trắng ở vị trí tiếp theo, nó sẽ trả về 0, có nghĩa là đó là một con đường chết.

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

Hãy thử ở đây (Không chạy đủ nhanh cho Ideone nên giá trị cuối cùng không được in, có vẻ như giải pháp của tôi không tốt lắm!)


Tôi đồng tình lên tới 6148 và tôi chưa tạo ra bất kỳ giá trị nào ngoài điều đó.
Peter Taylor

@PeterTaylor Vâng Agamelom nói rằng nó phải là 3, 28, 408 vì vậy tôi nghi ngờ 6148 là đúng. Tôi tự hỏi những gì chúng ta đang làm sai?
cmxu

Khá nhanh sau đó là của tôi. +1 ngay cả khi tôi không đồng ý về kết quả
edc65

Xin chào! Tôi chưa bao giờ nói đó là 28, 408 Trình tự đúng là 3,30,410, ...
Agamelom Hayopadhyay

Bạn nói, @ edc65 có các giá trị đúng và giá trị của anh ta là 28, 408?
cmxu

4

C ++ với pthreads, n = 147 156

Kết quả mới nhất là từ việc chạy mã giống như trước đây trên máy bò. Điều này hiện đã được chạy trên máy tính để bàn với i7 lõi ​​tứ (Core i7-4770), có tốc độ n = 156 trong 120 giây. Kết quả là:

785810368888248234969622509064814231709342669126944160689354425709131590643177370267626619864305813618736515156056592289185248184704932154134728284848

Đây là sử dụng một thuật toán lập trình động. Ban đầu tôi đã suy nghĩ về cách tiếp cận trong đó kết quả sẽ được xây dựng theo từng hàng, nhưng tôi không bao giờ có thể tìm ra cách mở rộng giải pháp mà không theo dõi hàng tấn trạng thái.

Những hiểu biết quan trọng cho phép một giải pháp hiệu quả hợp lý là:

  • Vì những con tốt trên hình vuông màu đen chỉ có thể tấn công những con tốt trên những hình vuông màu đen khác, và điều tương tự cũng đúng với hình vuông màu trắng, hình vuông màu đen và trắng là độc lập và có thể được xử lý riêng. Và vì chúng tương đương nhau, chúng ta chỉ cần xử lý một trong hai.
  • Vấn đề trở nên dễ dàng hơn nhiều khi xử lý bảng chéo theo đường chéo.

Nếu bạn nhìn vào một đường chéo của một cấu hình hợp lệ, nó luôn bao gồm một chuỗi các con tốt màu đen theo sau là một chuỗi các con tốt trắng (trong đó một trong hai chuỗi cũng có thể trống). Nói cách khác, mỗi đường chéo có thể được đặc trưng hoàn toàn bởi số lượng con tốt của nó.

Do đó, trạng thái được theo dõi cho mỗi đường chéo là số lượng cấu hình cầm đồ hợp lệ cho mỗi kết hợp:

  • Số lượng con tốt màu đen trong hàng (hay nói cách khác, vị trí trong đường chéo phân tách những con tốt màu đen với những con tốt màu trắng).
  • Tổng số pawns đen được sử dụng. Chúng ta cần theo dõi toàn bộ mọi thứ trên mỗi con số cầm đồ bởi vì chúng ta chỉ cần số lượng cầm đồ đen và cầm đồ trắng bằng nhau ở cuối. Trong khi xử lý các đường chéo, số lượng có thể khác nhau, và cuối cùng vẫn dẫn đến các giải pháp hợp lệ.

Khi bước từ một đường chéo này sang đường chéo tiếp theo, có một ràng buộc khác để xây dựng các giải pháp hợp lệ: Vị trí tách các con tốt đen khỏi các con tốt trắng không thể tăng. Vì vậy, số lượng cấu hình hợp lệ được tính bằng tổng cấu hình hợp lệ của đường chéo trước cho các vị trí bằng hoặc lớn hơn.

Bước DP cơ bản sau đó rất đơn giản. Mỗi giá trị trong một đường chéo chỉ là tổng của các giá trị từ đường chéo trước. Phần hơi đau đớn duy nhất là tính toán các chỉ số và phạm vi vòng lặp một cách chính xác. Vì chúng tôi đang làm việc trên các đường chéo, độ dài tăng trong nửa đầu của phép tính và giảm cho nửa sau, điều này làm cho việc tính toán các vòng lặp trở nên cồng kềnh hơn. Cũng có một số cân nhắc cho các giá trị ở ranh giới của bảng, vì chúng chỉ có hàng xóm chéo ở một bên khi bước từ đường chéo sang đường chéo.

Dung lượng bộ nhớ được sử dụng là O (n ^ 3). Tôi giữ hai bản sao dữ liệu trạng thái và ping pong giữa chúng. Tôi tin rằng nó có thể hoạt động với một thể hiện duy nhất của dữ liệu trạng thái. Nhưng bạn sẽ phải rất cẩn thận rằng không có giá trị nào được cập nhật trước khi các giá trị cũ được tiêu thụ hoàn toàn. Ngoài ra, nó sẽ không hoạt động tốt cho xử lý song song mà tôi đã giới thiệu.

Độ phức tạp thời gian chạy là ... đa thức. Có 4 vòng lặp lồng nhau trong thuật toán, vì vậy, ngay từ cái nhìn đầu tiên, nó sẽ trông giống như O (n ^ 4). Nhưng rõ ràng là bạn cần những gợi ý ở những kích thước này và bản thân các con số cũng dài hơn ở những kích thước lớn hơn. Số chữ số trong kết quả dường như tăng tỷ lệ thuận với n, điều này sẽ làm cho toàn bộ điều O (n ^ 5). Mặt khác, tôi tìm thấy một số cải tiến hiệu suất, giúp tránh đi qua toàn bộ các vòng lặp.

Vì vậy, trong khi đây vẫn là một thuật toán khá đắt tiền, nó sẽ xa hơn nhiều so với các thuật toán liệt kê các giải pháp, tất cả đều theo cấp số nhân.

Một số lưu ý khi thực hiện:

  • Mặc dù có thể có tới 2 * n ^ 2 con tốt màu đen trên các ô vuông màu đen, tôi chỉ tính các số cấu hình lên đến n ^ 2 con tốt. Vì có sự đối xứng giữa những con tốt và đen, nên số lượng cấu hình cho k và 2 * n ^ 2-k là như nhau.
  • Số lượng các giải pháp được tính ở cuối từ số lượng cấu hình trên các ô vuông màu đen dựa trên sự đối xứng tương tự. Tổng số giải pháp (cần có 2 * n ^ 2 pawns cho mỗi màu) là số lượng cấu hình cho k pawns đen trên một màu của hình vuông nhân với số lượng cấu hình cho pawns 2 * n ^ 2 k trên màu khác của hình vuông, tổng hợp trên tất cả k.
  • Ngoài việc chỉ lưu trữ số lượng cấu hình trên mỗi vị trí đường chéo và số cầm đồ, tôi còn lưu trữ phạm vi số cầm đồ có cấu hình hợp lệ cho mỗi vị trí. Điều này cho phép cắt giảm phạm vi của vòng lặp bên trong đáng kể. Không có điều này, tôi thấy rằng rất nhiều số không đã được thêm vào. Tôi đã có một cải thiện hiệu suất rất đáng kể từ điều này.
  • Thuật toán song song khá tốt, đặc biệt là ở kích thước lớn. Các đường chéo phải được xử lý tuần tự, do đó, có một rào cản ở cuối mỗi đường chéo. Nhưng các vị trí trong đường chéo có thể được xử lý song song.
  • Hồ sơ cho thấy rằng nút cổ chai rõ ràng trong việc thêm các giá trị bigint. Tôi đã chơi xung quanh với một số biến thể của mã, nhưng nó không được tối ưu hóa nhiều. Tôi nghi ngờ rằng có thể có một sự cải tiến đáng kể từ lắp ráp nội tuyến để sử dụng các bổ sung 64 bit với carry.

Mã thuật toán chính. THREADSkiểm soát số lượng luồng được sử dụng, trong đó số lượng lõi CPU phải là điểm khởi đầu hợp lý:

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

Điều này cũng cần một lớp học bigint mà tôi đã viết cho mục đích này. Lưu ý rằng đây không phải là một lớp bigint mục đích chung. Nó chỉ đủ để hỗ trợ các hoạt động được sử dụng bởi thuật toán cụ thể này:

#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

Tưởng tượng

Đây là một bài viết ban đầu thiết lập khung. Tôi nghĩ rằng thủ tục này là một tương đối tốt, nhưng việc thực hiện ngay bây giờ là rất tệ. Có lẽ tôi cần cố gắng giảm thiểu số lượng tính toán mà tôi đang thực hiện và thay vào đó chỉ cần vượt qua nhiều hằng số.

Chiến lược

Về cơ bản, mỗi con tốt trắng phải tấn công những con tốt khác. Vì vậy, tôi bắt đầu bằng cách đặt một con tốt trắng, đặt những con tốt ở mỗi nơi nó tấn công, và về cơ bản là điền vào bảng với tất cả những nơi mà một con tốt trắng đã đi. Tôi dừng lại nếu tôi đã thêm quá nhiều con tốt trắng. Nếu, vào cuối của điều này, tôi có chính xác 2n ^ 2 pawns, đó là một giải pháp. Nếu ít hơn thế, hãy thêm một con tốt trắng khác ở đâu đó, điền vào tất cả các địa điểm cần thiết của anh ta và đếm lại. Tôi phân chia đệ quy mỗi lần tìm thấy điền với ít hơn 2n ^ 2 và tính toán số lượng giải pháp có và không có con tốt cuối cùng tôi đã thêm.

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
  }

}

Đầu ra

Chỉ làm cho đến 5 ngay bây giờ, nhưng tôi nghĩ rằng hầu hết các vấn đề là trong việc thực hiện.

3
30
410
6148
96120

Kiểm tra


Đó cũng là chiến lược của tôi, nhưng dường như quá chậm so với các giải pháp khác được đăng ở đây.
edc65

@ edc65 Phương pháp tiếp cận liệt kê các giải pháp sẽ không có cơ hội. Nếu có bất kỳ nghi ngờ nào, số tuyệt đối được tạo ra bởi thuật toán của frageum là bằng chứng cho điều đó. Một số loại thuật toán lập trình động tính toán số lượng giải pháp mà không liệt kê chúng là cách để đi đến đây.
Reto Koradi
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.