เบี้ยมากเกินไปบนกระดานหมากรุก


10

เมื่อกำหนดจำนวนเต็ม 2n ให้ค้นหาจำนวนวิธีที่เป็นไปได้ที่เบี้ยสีดำ 2n ^ 2 และเบี้ยสีขาว 2n ^ 2 สามารถจัดวางบนกระดานหมากรุก 2n โดย 2n โดยที่จำนำไม่ได้โจมตีอีก

  • โรงรับจำนำสีดำสามารถโจมตีโรงรับจำนำสีขาวเท่านั้นและในทางกลับกัน
  • กฎการโจมตีตามปกติของหมากรุกคือจำนำสีขาวจู่โจมในแนวทแยงมุมด้านหน้าทันทีและจำนำโจมตีสี่เหลี่ยมจัตุรัสสีดำทันทีทแยงมุมย้อนหลัง (เท่าที่เห็นโดยผู้สังเกตการณ์สีขาว)
  • การหมุนทั้งหมดการสะท้อนกลับนับเป็นความแตกต่าง

โปรแกรมที่สามารถส่งออกการกำหนดค่าที่เป็นไปได้ทั้งหมดสำหรับค่าสูงสุด 2n ใน 120 วินาทีชนะ (ยินดีต้อนรับทุกโปรแกรม)

ตัวอย่างเช่นโปรแกรมของอลิซสามารถจัดการได้มากถึง n = 16 ภายใน 120 วินาทีในขณะที่บ๊อบสามารถจัดการได้มากถึง n = 20 ภายในเวลาเดียวกัน บ๊อบชนะ

แพลตฟอร์ม: Linux 2.7GHz @ 4 CPUs


2
รูปแบบผลลัพธ์คืออะไร
Doorknob

2
สำหรับการทดสอบ: มีคนที่คิดเกี่ยวกับตัวเลขที่เกี่ยวข้องหรือไม่ ฉันพบโซลูชัน 3 รายการสำหรับ 2x2 และ 28 โซลูชันสำหรับ 4x4
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

คำตอบ:


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จะต้อง +/- 1 จากตำแหน่งในคอลัมน์C - 1 เส้นยังสามารถขึ้นไปบนแถวจินตนาการสองแถวที่ด้านบนของกระดาน

เราสามารถใช้การเขียนโปรแกรมแบบไดนามิกเพื่อค้นหาจำนวนวิธีในการวาดเส้นบนคอลัมน์cแรกที่มีp pawns ในคอลัมน์เหล่านั้นคือที่ความสูงhบนคอลัมน์c th โดยใช้ผลลัพธ์สำหรับคอลัมน์c - 1 , ความสูงh + / - 1และหมายเลขของผู้จำนำพี - เอช


คุณสามารถแบ่งปันหมายเลขสำหรับ n = 87 ได้ไหม หรืออย่างน้อยคำสั่งของขนาด? นั่นจะต้องเป็นจำนวนมาก ...
Reto Koradi

ฉันสับสนเล็กน้อยเกี่ยวกับสิ่งที่คุณทำที่นี่ แต่มันน่าประทับใจมาก!
cmxu

ฉันคิดว่าฉันจะได้คำอธิบายส่วนใหญ่ของคุณยกเว้นเมื่อคุณพูดว่า "เส้นยังสามารถขึ้นไปบนสองแถวจินตภาพเหนือบนกระดาน"
cmxu

@ Changming นั่นหมายความว่าไม่มีการจำนำในคอลัมน์นั้นเท่านั้น
feersum

@feersum ฉันเห็นว่าเหมาะสมกว่าฉันจะดูว่าฉันสามารถทำงานผ่านตรรกะและดูว่าฉันสามารถหาวิธีที่จะใช้มันได้เร็วขึ้น
cmxu

5

ชวา

ปัจจุบันรหัสของฉันยาวและน่าเบื่อมากฉันกำลังพยายามทำให้เร็วขึ้น ฉันใช้วิธีเรียกซ้ำเพื่อหาค่า มันคำนวณ 5 ครั้งแรกภายใน 2 หรือ 3 วินาที แต่มันก็ช้าลงมากในภายหลัง นอกจากนี้ฉันยังไม่แน่ใจว่าถ้าตัวเลขถูกต้อง แต่ไม่กี่คนแรกที่ดูเหมือนจะสอดคล้องกับความคิดเห็น ข้อเสนอแนะใด ๆ ยินดีต้อนรับ

เอาท์พุต

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 และฉันยังไม่ได้สร้างคุณค่าใด ๆ
Peter Taylor

@PeterTaylor Well Agnishom บอกว่าควรเป็น 3, 28, 408 ดังนั้นฉันสงสัยว่า 6148 นั้นถูกต้อง ฉันสงสัยว่าเราทั้งคู่ทำอะไรผิด?
cmxu

ค่อนข้างเร็วกว่าของฉัน +1 แม้ว่าฉันไม่เห็นด้วยกับผลลัพธ์
edc65

สวัสดี! ฉันไม่เคยบอกว่ามันคือ 28, 408 ลำดับที่ถูกต้องคือ 3,30,410, ...
Agnishom Chattopadhyay

คุณบอกว่า @ edc65 มีค่าที่ถูกต้องและค่าของเขาคือ 28, 408?
cmxu

4

C ++ พร้อม pthreads, n = 147 156

ผลลัพธ์ล่าสุดมาจากการใช้รหัสเดียวกันกับที่เคยทำบนเครื่อง beefier ตอนนี้ถูกเรียกใช้บนเดสก์ท็อปที่มี i7 แบบ quad-core (Core i7-4770) ซึ่งได้ n = 156 ใน 120 วินาที ผลลัพธ์คือ:

7858103688882482349696225090648142317093426691269441606893544257091315906431773702676266198643058148987365151560565922891852481847049321541347582728793175114543840164406674137410614843200

นี่คือการใช้อัลกอริทึมการเขียนโปรแกรมแบบไดนามิก ตอนแรกฉันไตร่ตรองแนวทางที่ผลลัพธ์จะถูกสร้างขึ้นทีละแถว แต่ฉันไม่สามารถหาวิธีที่จะขยายการแก้ปัญหาได้โดยไม่ต้องติดตามสถานะใด ๆ

ข้อมูลเชิงลึกที่สำคัญที่เปิดใช้งานโซลูชันที่มีประสิทธิภาพเหมาะสมคือ:

  • เนื่องจากการจำนำบนสี่เหลี่ยมสีดำสามารถโจมตีได้เพียงการจำนำบนสี่เหลี่ยมสีดำอื่น ๆ เท่านั้นและเหมือนกันกับสี่เหลี่ยมสีขาวสี่เหลี่ยมสีดำและสีขาวจึงมีความเป็นอิสระและสามารถประมวลผลแยกกันได้ และเนื่องจากมันเท่ากันเราจึงต้องดำเนินการหนึ่งในสอง
  • ปัญหาจะง่ายขึ้นมากเมื่อประมวลผลในแนวทแยงของบอร์ดในแนวทแยง

หากคุณดูที่เส้นทแยงมุมหนึ่งของการกำหนดค่าที่ถูกต้องมันจะประกอบด้วยลำดับของเบี้ยสีดำตามด้วยลำดับของเบี้ยสีขาว (ซึ่งทั้งสองลำดับสามารถว่างเปล่าได้) กล่าวอีกนัยหนึ่งว่าแต่ละเส้นทแยงมุมสามารถจำแนกได้อย่างชัดเจนด้วยจำนวนเบี้ยสีดำ

ดังนั้นสถานะที่ถูกติดตามสำหรับแต่ละเส้นทแยงมุมคือจำนวนของการกำหนดค่าจำนำที่ถูกต้องสำหรับแต่ละชุดของ:

  • จำนวนเบี้ยสีดำในแถว (หรือกล่าวอีกนัยหนึ่งตำแหน่งภายในเส้นทแยงมุมที่แยกเบี้ยสีดำออกจากเบี้ยสีขาว)
  • จำนวนทั้งหมดของเบี้ยที่ใช้ไป เราจำเป็นต้องติดตามทุกสิ่งต่อการจำนำเพราะเราต้องการจำนวนเบี้ยสีดำและจำนำสีขาวเท่ากันในตอนท้าย ในขณะที่การประมวลผลเส้นทแยงมุมการนับสามารถแตกต่างกันและยังคงส่งผลให้การแก้ปัญหาที่ถูกต้องในที่สุด

เมื่อก้าวจากเส้นทแยงมุมหนึ่งไปอีกเส้นหนึ่งมีข้อ จำกัด อีกประการหนึ่งในการสร้างวิธีแก้ปัญหาที่ถูกต้องคือ: ตำแหน่งที่แยกเบี้ยสีดำออกจากเบี้ยสีขาวไม่สามารถเพิ่มได้ ดังนั้นจำนวนการกำหนดค่าที่ถูกต้องจะถูกคำนวณเป็นผลรวมของการกำหนดค่าที่ถูกต้องของเส้นทแยงมุมก่อนหน้าสำหรับตำแหน่งที่เท่ากันหรือใหญ่กว่า

ขั้นตอนพื้นฐาน DP นั้นง่ายมาก แต่ละค่าในแนวทแยงเป็นเพียงผลรวมของค่าจากเส้นทแยงมุมก่อนหน้า ส่วนที่เจ็บปวดเพียงอย่างเดียวคือการคำนวณดัชนีและช่วงลูปอย่างถูกต้อง เนื่องจากเรากำลังทำงานกับ diagonals ความยาวจะเพิ่มขึ้นในช่วงครึ่งแรกของการคำนวณและลดลงสำหรับครึ่งหลังซึ่งทำให้การคำนวณของลูปมีช่วงที่ยุ่งยากมากขึ้น นอกจากนี้ยังมีข้อควรพิจารณาบางประการเกี่ยวกับค่าที่ขอบเขตของบอร์ดเนื่องจากมีเพียงเพื่อนบ้านที่เป็นแนวทแยงมุมด้านเดียวเมื่อก้าวจากแนวทแยงมุมไปสู่แนวทแยง

จำนวนหน่วยความจำที่ใช้คือ O (n ^ 3) ฉันเก็บข้อมูลสถานะสองชุดและปิงปองระหว่างสองชุด ฉันเชื่อว่ามันเป็นไปได้ที่จะทำงานกับอินสแตนซ์เดียวของข้อมูลสถานะ แต่คุณจะต้องระมัดระวังเป็นอย่างยิ่งว่าจะไม่มีการปรับปรุงค่าใด ๆ ก่อนที่ค่าเดิมจะถูกใช้จนหมด นอกจากนี้มันจะไม่ทำงานได้ดีสำหรับการประมวลผลแบบขนานที่ฉันแนะนำ

ความซับซ้อนของรันไทม์คือ ... พหุนาม อัลกอริทึมมี 4 ลูปซ้อนกันดังนั้นตั้งแต่แรกเห็นมันจะดูเหมือน O (n ^ 4) แต่เห็นได้ชัดว่าคุณต้องการภาพใหญ่ในขนาดเหล่านี้และตัวเลขก็ยิ่งยาวขึ้นด้วยขนาดที่ใหญ่ขึ้น จำนวนหลักในผลลัพธ์ดูเหมือนจะเพิ่มขึ้นตามสัดส่วนเป็น 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 ที่ฉันเขียนเพื่อจุดประสงค์นี้ โปรดทราบว่านี่ไม่ใช่ชั้นเรียนที่มีวัตถุประสงค์โดยทั่วไป มันเพียงพอที่จะรองรับการดำเนินการที่ใช้โดยอัลกอริทึมเฉพาะนี้:

#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

Fantom

นี่คือโพสต์เริ่มต้นที่กำหนดกรอบการทำงาน ฉันคิดว่าขั้นตอนนั้นค่อนข้างดี แต่การใช้งานในตอนนี้ค่อนข้างแย่มาก ฉันอาจต้องพยายามลดจำนวนการคำนวณที่ฉันทำไว้ให้น้อยที่สุดและให้ค่าคงที่เพิ่มขึ้นแทน

กลยุทธ์

โดยพื้นฐานแล้วโรงรับจำนำสีขาวแต่ละแห่งจะต้องโจมตีโรงรับจำนำสีขาวอื่น ๆ ดังนั้นฉันจึงเริ่มต้นด้วยการวางจำนำสีขาววางเบี้ยในแต่ละสถานที่ที่มันโจมตีและเป็นหลักเติมในกระดานกับสถานที่ทั้งหมดจำนำสีขาวที่จะไป ฉันหยุดถ้าฉันได้เพิ่มเบี้ยสีขาวจำนวนมากเกินไปแล้ว หากในตอนท้ายของเรื่องนี้ฉันมีเบี้ย 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.