棋盘上的棋子太多


10

给定一个整数2n,找到在2n x 2n的棋盘上可以排列2n ^ 2个黑色棋子和2n ^ 2个白色棋子的可能方式的数量,这样就不会有其他棋子攻击对方。

  • 黑色的棋子只能攻击白色的棋子,反之亦然。
  • 遵循通常的棋牌进攻规则,即白色棋子在对角线前面立即攻击正方形,而黑色棋子在对角线后立即攻击正方形(如白色观察者所见)。
  • 所有旋转,反射算作不同的。

可以在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。
彼得·泰勒2015年

1
我的代码生成了前几个代码:3、30、410、6148、96120、1526700。尽管如此,我无法进行检查。任何人都一样吗?
cmxu 2015年

1
为了明确运算符的优先级,当您说出2n^2pawn时,是(2n)^2还是2(n^2)??
Reto Koradi 2015年

Answers:


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)运算的动态编程方案来计算将ppawn放在的一种颜色的正方形上的方式0 <= p <= n^2。我认为应该有可能更有效地做到这一点。

在这里查看结果。

说明

在有效的解决方案中,每列中最下面的白色棋子必须形成如下的锯齿形线:

典当行

也就是说,c列中的线的高度必须与c-1列中的位置的+/- 1。这条线也可以进入板顶上方的两个假想行。

我们可以使用动态编程来找到的路的数目来绘制在第一线ç包括列p上的那些列棋子,是在高度ħÇ列,使用效果列Ç - 1,高度H + /-1p-h的数量


可以共享n = 87的数字吗?或者至少是数量级?这个数目必须非常大...
Reto Koradi

我对您在这里的工作感到有些困惑,但这非常令人印象深刻!
cmxu

我想我得到了您的大部分解释,除了您说“这条线也可以走到板顶上方的两个假想行”时
cmxu

@Changming,仅表示该列中没有典当。
feersum 2015年

@feersum我认为这更有意义,我将看看是否可以通过逻辑工作,并确定是否可以找到更快地实现它的方法。
cmxu

5

爪哇

目前,我的代码非常冗长乏味,我正在努力使其速度更快。我使用递归方法来查找值。它会在2到3秒钟内计算出前5个,但之后会变慢得多。另外,我不确定数字是否正确,但是前几个似乎与评论一致。欢迎任何建议。

输出量

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

说明

基本思想是递归。本质上,您从一个空板开始,一个全零的板。递归方法只是检查是否可以将黑色或白色的棋子放置在下一个位置,如果只能放置一种颜色,则将其放置在其中并自行调用。如果它可以同时使用两种颜色,则它会自称两次,每种颜色一次。每次调用它自己时,都会减小左方的正方形,并减小适当的颜色。当它装满整个电路板时,它会返回当前计数+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 2015年

比我快得多。即使我不同意结果也+1
edc65

你好!我从未说过它是28、408,正确的顺序是3,30,410,...
Agnishom Chattopadhyay 2015年

您说过,@ edc65的值正确,他的值是28、408?
cmxu 2015年

4

C ++与并行线程,N = 147 156

最新结果是在功能更强大的计算机上运行与以前相同的代码。现在,该程序在具有四核i7(酷睿i7-4770)的台式机上运行,​​在120秒内达到n = 156。结果是:

7858103688882482349696225090648148142317093426691269441606893544257091315906431773702676266198643058148987365151560565922891852481847049321541347582728793175114543840164406674137410614843200

这是使用动态编程算法。我最初考虑的是将结果逐行构建的方法,但是如果没有跟踪大量状态,我永远无法想出一种扩展解决方案的方法。

实现合理有效的解决方案的关键见解包括:

  • 由于黑色正方形上的棋子只能攻击其他黑色正方形上的棋子,白色正方形也是如此,因此黑色和白色正方形是独立的,可以单独处理。并且由于它们是等效的,因此我们只需要处理两者之一即可。
  • 当按对角线方向处理电路板时,问题变得更加容易。

如果查看有效配置的一个对角线,则它总是由一系列的黑色典当组成,之后是一系列的白色典当(其中任一序列也可以为空)。换句话说,每个对角线都可以通过其黑色棋子的数量充分表征。

因此,每个对角线跟踪的状态是以下每种组合的有效典当配置数:

  • 该行中黑色棋子的数量(或换句话说,对角线内将黑色棋子与白色棋子分隔开的位置)。
  • 使用的黑色典当总数。我们需要跟踪每个棋子计数的全部内容,因为在最后只需要相等数量的黑色棋子和白色棋子即可。在处理对角线时,计数可能会有所不同,最终仍会产生有效的解决方案。

从一个对角线移动到下一个对角线时,要建立有效的解决方案还有另一个约束:将黑卒与白卒分开的位置不能增加。因此,有效配置的数量将计算为等于或更大位置的前一个对角线的有效配置之和。

这样,基本的DP步骤就非常简单。对角线上的每个值仅是前一个对角线的值之和。唯一痛苦的部分是正确计算索引和循环范围。由于我们正在处理对角线,因此长度在计算的上半部分增加,而在下半部分减小,这使得循环范围的计算更加麻烦。还需要考虑板边界的值,因为从对角线到对角线时,它们的一侧只有对角线邻点。

使用的内存量为O(n ^ 3)。我保留了状态数据的两个副本,并在它们之间进行了乒乓球。我相信可以使用状态数据的单个实例进行操作。但是您必须非常小心,在完全使用旧值之前不会更新任何值。另外,对于我介绍的并行处理,它也不能很好地工作。

运行时复杂度是...多项式。该算法中有4个嵌套循环,因此乍一看它看起来像O(n ^ 4)。但是您显然需要这些大小的bigints,并且数字本身也随着更大的大小而变长。结果中的位数似乎与n大致成比例增加,这将使整个事物成为O(n ^ 5)。另一方面,我发现了一些性能改进,从而避免了遍历所有循环的全部范围。

因此,尽管这仍然是一个相当昂贵的算法,但它比枚举所有指数式解决方案的算法要远得多。

有关实现的一些注意事项:

  • 虽然在黑色方块上最多可以有2 * n ^ 2个黑色棋子,但我最多只能计算n ^ 2个黑色棋子的配置号。由于黑白棋子之间存在对称性,因此k和2 * n ^ 2-k的配置计数相同。
  • 最后,基于相似的对称性,根据黑色正方形上的配置计数计算出解决方案的数量。解决方案的总数(每种颜色需要具有2 * n ^ 2个典当)是一种颜色正方形上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的填充时,我都会递归拆分,并计算添加或不添加最后一个pawn时的解决方案数量。

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.