체스 판에 너무 많은 폰


10

정수 2n이 주어지면, 2n ^ 2 개의 검은 폰과 2n ^ 2 개의 흰색 폰이 2n x 2n 체스 판에 배열되어 다른 폰이 다른 공격을받지 않도록 할 수있는 방법의 수를 찾으십시오.

  • 검은 전당포는 흰색 전당포 만 공격 할 수 있으며 그 반대도 가능합니다.
  • 공격의 일반적인 체스 규칙은 다음과 같습니다. 즉, 흰색 폰은 앞쪽 대각선 바로 앞의 사각형을 공격하고, 검은 색 폰은 앞뒤 대각선으로 사각형을 공격합니다 (흰색 관찰자가 볼 수 있음).
  • 모든 회전, 반사는 뚜렷한 것으로 간주됩니다.

120 초 안에 2n의 최고 값에 대해 가능한 모든 구성을 출력 할 수있는 프로그램이 승리합니다. (그러나 모든 프로그램은 환영합니다)

예를 들어 Alice의 프로그램은 120 초 내에 n = 16까지 처리 할 수 ​​있고 Bob은 동시에 n = 20까지 처리 할 수 ​​있습니다. 밥이 이긴다.

플랫폼 : Linux 2.7GHz @ 4 CPU


2
출력 형식은 무엇입니까?
Doorknob

2
테스트를 위해 : 누군가 관련된 숫자에 대한 아이디어가 있습니까? 2x2 용 솔루션 3
개와

1
@ edc65, 나는 그것을 3, 30, 410으로 만듭니다. 나는 다른 방법으로 3과 30을 확인했습니다.
피터 테일러

1
3, 30, 410, 6148, 96120, 1526700 코드를 생성했습니다. 그럼에도 불구하고 확인할 방법이 없습니다. 누구나 같은 것을 얻습니까?
cmxu

1
2n^2폰 을 말할 때 연산자 우선 순위를 명확히하려면 (2n)^2또는 2(n^2)입니까?
Reto Koradi 2016 년

답변:


9

Java, 내 컴퓨터의 n = 87

n = 87의 결과는

62688341832480765224168252369740581641682638216282495398959252035334029997073369148728772291668336432168


import java.math.BigInteger;

public class NonattackingPawns {

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

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

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

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

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

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

        }

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

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

}

현재 O (n ^ 4) 연산을 수행하는 다이나믹 프로그래밍 방식을 사용하여 p에 대해 한 색상의 사각형에 폰 을 배치하는 방법을 계산합니다 0 <= p <= n^2. 훨씬 더 효율적으로 수행 할 수 있어야한다고 생각합니다.

결과를 확인하십시오.

설명

유효한 솔루션에서 각 열의 가장 낮은 흰색 폰은 다음과 같이 지그재그 라인을 형성해야합니다.

폰 라인

즉, c 열의 행 높이는 c-1 열의 위치에서 +/- 1이어야합니다 . 선은 또한 보드 상단 위의 두 개의 가상 줄로 갈 수 있습니다.

우리는 처음에 라인 그리는 방법들의 수를 찾는 동적 프로그래밍을 사용하여 (C)를 포함하는 열 (P)의 높이로하고, 그 열에 졸 HC 칼럼의 결과를 사용, 열 번째 C 1 - , 고도의 시간 + /-1 및 폰 수 p-h .


n = 87의 숫자를 공유 할 수 있습니까? 아니면 최소한 규모의? 그것은 매우 많은 수 ...이어야한다
레토 Koradi

나는 당신이 여기서하는 일에 약간 혼란 스럽지만 매우 인상적입니다!
cmxu

"선이 보드 상단의 두 개의 가상 줄로 갈 수도 있습니다"
cmxu

@Changming은 해당 열에 폰이 없음을 의미합니다.
feersum

@feersum 더 이해가되는 것을 보았습니다. 논리를 통해 작업 할 수 있는지, 더 빨리 구현할 수있는 방법을 찾을 수 있는지 확인할 것입니다.
cmxu

5

자바

현재 내 코드는 매우 길고 지루하며 더 빨리 만들기 위해 노력하고 있습니다. 값을 찾기 위해 재귀 적 방법을 사용합니다. 2 초 또는 3 초 내에 처음 5를 계산하지만 나중에 훨씬 느려집니다. 또한, 숫자가 올바른지 확실하지 않지만 처음 몇 개는 주석과 일치하는 것 같습니다. 어떤 제안이라도 환영합니다.

산출

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

설명

기본 아이디어는 재귀입니다. 기본적으로 빈 보드, 모두 0이있는 보드로 시작합니다. 재귀 메서드는 다음 위치에 검은 색 또는 흰색 폰을 넣을 수 있는지 확인하고, 한 가지 색상 만 넣을 수 있으면 거기에 넣고 호출합니다. 두 색상을 모두 넣을 수 있으면 각 색상마다 하나씩 두 번 호출됩니다. 자신을 호출 할 때마다 왼쪽 사각형과 적절한 색상이 줄어 듭니다. 보드 전체를 채운 후 현재 카운트 + 1을 반환합니다. 다음 위치에 검은 색 또는 흰색 폰을 넣을 방법이 없다는 것을 알게되면 0을 반환합니다.

암호

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

여기에서 시도하십시오 (Ideone에 대해 충분히 빨리 실행되지 않으므로 마지막 값이 인쇄되지 않습니다. 제 솔루션이 좋지 않은 것 같습니다!)


나는 6148까지 동의했으며 아직 그 이상의 가치를 얻지 못했습니다.
피터 테일러

@PeterTaylor Well Agnishom은 3, 28, 408이어야한다고 말하면서 6148이 옳지 않다고 생각합니다. 우리 둘 다 뭘 잘못하는지 궁금해?
cmxu

내 것보다 훨씬 빠릅니다. 결과에 동의하지 않더라도 +1
edc65 2016

여보세요! 나는, 408 28 말했다 결코 올바른 순서입니다 3,30,410 ...
Agnishom Chattopadhyay

당신은 @ edc65가 올바른 값을 가지고 있고 그의 값은 28, 408이라고 말했습니까?
cmxu 2016 년

4

pthreads 정보와 C ++, N = 147 156

최신 결과는 이전보다 더 강력한 머신에서 동일한 코드를 실행 한 결과입니다. 이제 쿼드 코어 i7 (코어 i7-4770)이있는 데스크톱에서 실행되었으며 120 초 동안 n = 156이되었습니다. 결과는 다음과 같습니다.

7858103688882482349696225090648142317093426691269441606893544257091315906431773702676266198643058148987365151560565922891852481847049321541347582728793175114543840164406674137410614843200

이것은 동적 프로그래밍 알고리즘을 사용하고 있습니다. 처음에는 결과가 행 단위로 작성되는 접근 방식에 대해 고민했지만 많은 상태를 추적하지 않고 솔루션을 확장하는 방법을 생각해 낼 수 없었습니다.

합리적으로 효율적인 솔루션을 가능하게 한 주요 통찰력은 다음과 같습니다.

  • 검은 사각형의 폰은 다른 검은 사각형의 폰만 공격 할 수 있고 흰색 사각형의 경우에도 마찬가지이므로 검은 색과 흰색 사각형은 독립적이며 별도로 처리 할 수 ​​있습니다. 그것들은 동등하기 때문에 우리는 둘 중 하나만 처리하면됩니다.
  • 대각선으로 보드를 대각선으로 처리 할 때 문제가 훨씬 쉬워집니다.

유효한 구성의 대각선 하나를 보는 경우 항상 검은 색 폰의 순서와 흰색 폰의 순서로 구성됩니다 (어느 순서도 비어있을 수 있음). 다시 말해, 각 대각선은 블랙 폰의 수에 의해 완전히 특성화 될 수 있습니다.

따라서 각 대각선에 대해 추적되는 상태는 다음의 각 조합에 대해 유효한 폰 구성의 수입니다.

  • 행에있는 검은 폰의 수 (즉, 검은 폰을 흰색 폰과 분리하는 대각선 내의 위치)입니다.
  • 사용 된 검은 폰의 총 수 우리는 맨 끝에 같은 수의 검은 폰과 흰색 폰이 필요하기 때문에 폰 수 당 모든 것을 추적해야합니다. 대각선을 처리하는 동안 개수가 다를 수 있으며 결국 유효한 솔루션이됩니다.

한 대각선에서 다음 대각선으로 스테핑 할 때 유효한 솔루션을 구축해야하는 또 다른 제약이 있습니다. 따라서 유효한 구성 수는 같거나 큰 위치에 대한 이전 대각선의 유효한 구성의 합으로 계산됩니다.

기본적인 DP 단계는 매우 간단합니다. 대각선의 각 값은 이전 대각선의 값 합계입니다. 다소 고통스러운 부분은 인덱스와 루프 범위를 올바르게 계산하는 것입니다. 대각선으로 작업하고 있기 때문에 계산의 전반부 동안 길이가 증가하고 후반부에서는 길이가 줄어들어 루프 범위 계산이 더 번거로워집니다. 대각선에서 대각선으로 밟을 때 한쪽에 대각선 이웃 만 있기 때문에 보드 경계의 값에 대한 고려 사항도 있습니다.

사용 된 메모리 양은 O (n ^ 3)입니다. 상태 데이터의 사본 두 개와 핑퐁을 유지합니다. 상태 데이터의 단일 인스턴스로 작동하는 것이 가능하다고 생각합니다. 그러나 이전 값이 완전히 소비되기 전에 값이 업데이트되지 않도록 매우주의해야합니다. 또한 내가 도입 한 병렬 처리에는 적합하지 않습니다.

런타임 복잡성은 다항식입니다. 알고리즘에는 4 개의 중첩 루프가 있으므로 처음에는 O (n ^ 4)처럼 보입니다. 그러나 분명히이 크기의 bigint가 필요하며 숫자 자체도 더 큰 크기로 길어집니다. 결과의 자릿수는 대략 n에 비례하여 증가하는 것으로 보이며, 이로 인해 모든 것이 O (n ^ 5)가됩니다. 반면에, 나는 모든 루프의 전체 범위를 거치지 않는 성능 향상을 발견했습니다.

따라서이 알고리즘은 여전히 ​​상당히 비싼 알고리즘이지만 솔루션을 열거하는 알고리즘보다 훨씬 더 넓습니다.

구현에 대한 몇 가지 참고 사항 :

  • 검은 사각형에는 최대 2 * n ^ 2 개의 검은 색 폰이있을 수 있지만, n ^ 2 개의 검은 색 폰까지만 구성 번호를 계산합니다. 블랙 폰과 화이트 폰 사이에 대칭이 있기 때문에 k와 2 * n ^ 2-k의 구성 카운트는 동일합니다.
  • 솔루션의 수는 유사한 대칭을 기반으로 검은 사각형의 구성 카운트에서 마지막에 계산됩니다. 솔루션의 총 수 (각 색상의 2 * n ^ 2 폰이 필요)는 한 사각형의 k 색상에 대한 k 블랙 폰의 구성 수에 2 * n ^ 2-k 블랙 폰의 구성 수를 곱한 값입니다. 모든 k에 대해 합산 된 다른 사각형 색상.
  • 대각선 위치 및 폰 수당 구성 수를 저장하는 것 외에도 위치 당 유효한 구성을 가진 폰 수의 범위를 저장합니다. 이것은 내부 루프의 범위를 실질적으로 감소시킨다. 이것이 없으면 많은 제로가 추가되고 있음을 알았습니다. 이것으로부터 성능이 크게 향상되었습니다.
  • 알고리즘은 특히 큰 크기에서 상당히 잘 병렬 처리됩니다. 대각선은 순차적으로 처리되어야하므로 각 대각선 끝에 장벽이 있습니다. 그러나 대각선 내의 위치는 병렬로 처리 될 수 있습니다.
  • 프로파일 링을 통해 병목 현상이 분명히 bigint 값을 추가하는 것으로 나타났습니다. 몇 가지 변형 코드를 가지고 놀았지만 크게 최적화되지 않았습니다. 캐리와 함께 64 비트 추가 기능을 사용하기 위해 인라인 어셈블리에서 크게 개선 될 수 있다고 생각합니다.

주요 알고리즘 코드. THREADS사용되는 스레드 수를 제어합니다. 여기서 CPU 코어 수는 합리적인 시작점이어야합니다.

#ifndef THREADS
#define THREADS 2
#endif

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

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

#include "BigUint.h"

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

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

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

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

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

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

    pthread_mutex_unlock(&ThreadMutex);
}
#endif

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        prevDiagMin = diagMin;
        prevDiagMax = diagMax;

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

    return 0;
}

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

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

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

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

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

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

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

    BarrierCount = THREADS;

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

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

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

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

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

        solCount += nComb;
    }

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

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

    BigUint res;
    countPawns(res);

    return 0;
}

이것은 또한이 목적으로 쓴 bigint 클래스가 필요합니다. 이것은 범용 bigint 클래스가 아닙니다. 이 특정 알고리즘에서 사용하는 작업을 지원하기에 충분합니다.

#ifndef BIG_UINT_H
#define BIG_UINT_H

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

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

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

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

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

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

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

        return *this;
    }

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

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

        m_size = rhs.m_size;

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

        return *this;
    }

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

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

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

        return *this;
    }

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

        uint64_t sum = 0;

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

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

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

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

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

            delete[] resValA;
        }

        m_size = resSize;

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

        return *this;
    }

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

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

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

        rMod = div;
    }

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

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

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

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

private:
    static const int MIN_CAP = 8;

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

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

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

            m_cap = newSize;
            m_valA = newValA;
        }

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

        m_size = newSize;
    }

    int m_size;
    int m_cap;

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

#endif // BIG_UINT_H

0

팬텀

다음은 프레임 워크를 설정하는 초기 게시물입니다. 나는 절차가 비교적 좋은 것이라고 생각하지만, 지금은 구현이 짜증납니다. 아마도 내가하고있는 계산의 수를 최소화하려고 시도하고 대신 더 많은 상수를 전달해야합니다.

전략

기본적으로 각 흰색 폰은 다른 흰색 폰을 공격해야합니다. 그래서 나는 흰색 폰을 배치하고, 공격하는 각 장소에 폰을 배치하고, 본질적으로 하얀 폰이있는 모든 장소로 보드를 채우는 것으로 시작합니다. 이미 흰색 폰을 너무 많이 추가하면 멈 춥니 다. 이것의 끝에 정확히 2n ^ 2 폰이 있다면, 그것은 해결책입니다. 그보다 적 으면 어딘가에 흰색 전당포를 추가하고 필요한 장소를 모두 채우고 다시 세십시오. 2n ^ 2 미만의 채우기가 발견 될 때마다 재귀 적으로 분할하고 마지막으로 추가 한 폰이 있거나없는 솔루션 수를 계산합니다.

암호

class main
{
  public  Void main(){

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

  }

  public static  Int calculate(Int n){

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

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

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


  }

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

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

}

산출

지금은 5로만 만들지 만 문제의 대부분은 구현에 있다고 생각합니다.

3
30
410
6148
96120

테스트


그것은 나의 전략이기도하지만 여기에 게시 된 다른 솔루션에 비해 너무 느린 것 같습니다.
edc65

@ edc65 솔루션을 열거하는 접근법에는 기회가 없습니다. 의심의 여지가 있다면, feersum의 알고리즘에 의해 생성 된 깎아 지른 숫자는 그 증거입니다. 솔루션을 열거하지 않고 솔루션 수를 계산하는 일종의 동적 프로그래밍 알고리즘이 여기에 있습니다.
Reto Koradi 2016 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.