チェス盤のポーンが多すぎる


10

整数2nを指定して、2n ^ 2の黒のポーンと2n ^ 2の白のポーンを2n x 2nのチェス盤に配置して、ポーンが他のポーンを攻撃しないようにする方法の数を見つけます。

  • 黒のポーンは白のポーンのみを攻撃でき、その逆も可能です。
  • 通常のチェスの攻撃ルールは次のとおりです。つまり、白いポーンは正面の対角線のすぐ後ろの正方形を攻撃し、黒いポーンはすぐ後ろの斜めの正方形を攻撃します(白い観察者が見るように)。
  • すべての回転、反射は個別としてカウントされます。

120秒で2nの最高値のすべての可能な構成を出力できるプログラムが勝利します。(ただし、すべてのプログラムを歓迎します)

たとえば、アリスのプログラムは120秒以内に最大n = 16を処理でき、ボブのプログラムは同じ時間内に最大n = 20を処理できます。ボブが勝ちます。

プラットフォーム:Linux 2.7GHz @ 4 CPU


2
出力形式は何ですか?
ドアノブ

2
テストのために:誰かが関与する数字について何か考えを持っていますか?2x2の3つのソリューションと4x4の28のソリューションを見つけた
edc65

1
@ edc65、私はそれを3、30、410にします。別の方法で3と30をチェックしました。
Peter Taylor

1
最初のいくつかのコード(3、30、410、6148、96120、1526700)をコードで生成しました。ただし、チェックする方法はありません。誰もが同じですか?
cmxu

1
演算子の優先順位を明確にするために、2n^2ポーンと言うとき、(2n)^2それは2(n^2)
Reto Koradi 2015年

回答:


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の1つの色の正方形にポーンを配置する方法を計算します0 <= p <= n^2。これをもっと効率的にできると思います。

ここで結果を確認してください。

説明

有効なソリューションでは、各列の一番下の白いポーンは、次のようなジグザグの線を形成する必要があります。

ポーンライン

つまり、列cの行の高さは、列c-1の位置から+/- 1でなければなりません。線は、ボードの上部にある2つの架空の行にも配置できます。

動的プログラミングを使用して、それらの列のpポーンを含む最初のc列に線を描画する方法の数を見つけることができます。これは、c番目の列の高さhにあり、列c-1、高さh +の結果を使用します。/-1、ポーンの数p-h


n = 87の数値を共有できますか?または、少なくとも桁で?それは非常に大きな数でなければなりません...
Reto Koradi

ここで何をしているのか少し混乱していますが、とても印象的です。
cmxu

「ボードの上部にある架空の2列に線が入ることもあります」を除いて、ほとんどの説明は
理解できたと思います

@Changming、それはその列にポーンがないことを意味するだけです。
feersum

@feersum私はもっと理にかなっていると思います。ロジックを処理できるかどうか、さらに高速に実装する方法を見つけることができるかどうかを確認します。
cmxu

5

ジャワ

現在、私のコードは非常に長く、面倒です。私はそれをより速くするために取り組んでいます。値を見つけるために再帰的な方法を使用しています。最初の5は2〜3秒以内に計算されますが、その後はかなり遅くなります。また、数字が正しいかどうかはまだわかりませんが、最初のいくつかはコメントと一致しているようです。どんな提案でも大歓迎です。

出力

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

説明

基本的な考え方は再帰です。基本的に、空のボード、すべてゼロのボードから始めます。再帰メソッドは、黒または白のポーンを次の位置に配置できるかどうかを確認するだけです。1つの色しか配置できない場合は、そこに配置して自分自身を呼び出します。両方の色を配置できる場合は、各色を1つずつ、2回呼び出します。自分自身を呼び出すたびに、左の四角と左の適切な色を減らします。ボード全体を埋めると、現在のカウント+ 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;
                }
            }
        }
    }
}

ここで試してください (Ideonの実行速度が十分でないため、最後の値が印刷されません。私のソリューションはあまり良くないようです!)


私は6148まで同意しますが、それ以上の値はまだ作成していません。
Peter Taylor

@PeterTaylor Well Agnishomは、3、28、408とすべきだと言っているので、6148が正しいとは思えません。どちらが間違っているのでしょうか。
cmxu 2015年

私よりもかなり速い。結果に同意しなくても+1
edc65

こんにちは!28、408だと言ったことはありません。正しい順序は3、30、410などです
。– Agnishom Chattopadhyay

@ edc65には適切な値があり、彼の値は28、408ですか?
cmxu 2015年

4

pスレッドとC ++、N = 147 156

最新の結果は、より強力なマシンで以前と同じコードを実行した結果です。これはクアッドコアi7(Core i7-4770)を搭載したデスクトップで実行され、120秒でn = 156になりました。結果は次のとおりです。

7858103688882482349696225090648142317093426691269441606893544257091315906431773702676266198643058148987365151560565922891852481847049321541347582728793175114543840164406674137410614843200

これは動的プログラミングアルゴリズムを使用しています。最初は結果が行ごとに構築されるアプローチを考えましたが、大量の状態を追跡せずにソリューションを拡張する方法を思いつくことはできませんでした。

かなり効率的なソリューションを可能にした主な洞察は次のとおりです。

  • 黒い正方形のポーンは他の黒い正方形のポーンしか攻撃できず、白い正方形の場合も同じであるため、黒い正方形と白い正方形は独立しており、別々に処理できます。また、これらは同等であるため、2つのうち1つを処理するだけで済みます。
  • 斜めにボードを斜めに処理すると、問題がはるかに簡単になります。

有効な構成の1つの対角線を見ると、それは常に一連の黒いポーンとそれに続く一連の白いポーンで構成されます(どちらのシーケンスも空にすることもできます)。つまり、各対角線は、黒のポーンの数によって完全に特徴付けることができます。

したがって、各対角線で追跡される状態は、次の組み合わせごとの有効なポーン構成の数です。

  • 行の黒のポーンの数(つまり、黒のポーンと白のポーンを区切る対角線内の位置)。
  • 使用された黒のポーンの総数。ポーンカウントごとに全体を追跡する必要があります。これは、最後に等しい数の黒いポーンと白いポーンしか必要ないためです。対角線を処理している間、カウントは異なる場合がありますが、最終的には有効なソリューションになります。

ある対角線から次の対角線に移動する場合、有効なソリューションを構築するための別の制約があります。黒いポーンと白いポーンを分離する位置は増加できません。したがって、有効な構成の数は、等しいかそれ以上の位置について、前の対角線の有効な構成の合計として計算されます。

その場合、基本的なDPステップは非常に簡単です。対角線の各値は、直前の対角線からの値の合計です。少しやっかいな部分は、インデックスとループ範囲を正しく計算することだけです。対角線で作業しているため、長さは計算の前半で増加し、後半で減少するため、ループ範囲の計算がより面倒になります。ボードの境界での値にはいくつかの考慮事項もあります。これは、対角線から対角線にステップするとき、片側に隣接する対角線のみがあるためです。

使用されるメモリの量はO(n ^ 3)です。私は状態データの2つのコピーを保持し、それらの間でピンポンを行います。状態データの単一のインスタンスで操作することは可能だと思います。ただし、古い値が完全に消費される前に値が更新されないように非常に注意する必要があります。また、紹介した並列処理ではうまく機能しません。

実行時の複雑さは...多項式です。アルゴリズムには4つのネストされたループがあるため、一見するとO(n ^ 4)のように見えます。ただし、これらのサイズではbigintが明らかに必要であり、数値自体もサイズが大きくなると長くなります。結果の桁数は、nにほぼ比例して増加するようで、全体をO(n ^ 5)にします。一方、すべてのループの全範囲を通過することを回避する、いくつかのパフォーマンスの向上を発見しました。

したがって、これはまだかなり高価なアルゴリズムですが、すべて指数関数的なソリューションを列挙するアルゴリズムよりもはるかに遠くまで到達します。

実装に関する注意事項:

  • 黒い四角には最大2 * n ^ 2個の黒いポーンが存在する可能性がありますが、私は最大n ^ 2個の黒いポーンの構成番号のみを計算します。黒と白のポーンの間には対称性があるため、kと2 * n ^ 2-kの構成カウントは同じです。
  • ソリューションの数は、同様の対称性に基づいて黒い四角形の構成カウントから最後に計算されます。ソリューションの総数(各色のポーンが2 * n ^ 2である必要がある)は、正方形の1つの色での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 2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.