Checker Challenge
Examine the 6x6 checkerboard below and note that the six checkers are arranged on the board so that one and only one is placed in each row and each column, and there is never more than one in any diagonal. (Diagonals run from southeast to northwest and southwest to northeast and include all diagonals, not just the major two.)
Column 1 2 3 4 5 6 ------------------------- 1 | | O | | | | | ------------------------- 2 | | | | O | | | ------------------------- 3 | | | | | | O | ------------------------- 4 | O | | | | | | ------------------------- 5 | | | O | | | | ------------------------- 6 | | | | | O | | -------------------------The solution shown above is described by the sequence 2 4 6 1 3 5, which gives the column positions of the checkers for each row from 1 to 6:
ROW 1 2 3 4 5 6 COLUMN 2 4 6 1 3 5 This is one solution to the checker challenge. Write a program that finds all unique solution sequences to the Checker Challenge (with ever growing values of N). Print the solutions using the column notation described above. Print the the first three solutions in numerical order, as if the checker positions form the digits of a large number, and then a line with the total number of solutions.
Special note: the larger values of N require your program to be especially efficient. Do not precalculate the value and print it (or even find a formula for it); that's cheating. Work on your program until it can solve the problem properly. If you insist on cheating, your login to the USACO training pages will be removed and you will be disqualified from all USACO competitions. YOU HAVE BEEN WARNED.
TIME LIMIT: 1 CPU second
PROGRAM NAME: checker
INPUT FORMAT
A single line that contains a single integer N (6 <= N <= 13) that is the dimension of the N x N checkerboard.SAMPLE INPUT (file checker.in)
6OUTPUT FORMAT
The first three lines show the first three solutions found, presented as N numbers with a single space between them. The fourth line shows the total number of solutions found.SAMPLE OUTPUT (file checker.out)
2 4 6 1 3 5 3 6 2 5 1 4 4 1 5 2 6 3 4HINTS (use them carefully!)
忘了说了,看了奶牛网的分析,忽然想起了,这八皇后问题是对称性的,只要求其一半×2就是结果了,也就是说着程序还可以优化
/* ID:nealgav1 PROG:checker LANG:C++ */ #include<cstdio> #include<cstring> #define N 42 bool l[42],r[42],t[42]; int m; int shu[15]; int ans; void dfs(int n) { if(n<=m) { for(int i=1;i<=m;i++) { if(!l[13+i]&&!r[13+i+n]&&!t[13+i-n])//列+两对角 只要三值不一,不冲突皆可 { l[13+i]=1;r[13+i+n]=1;t[13+i-n]=1; shu[n]=i;dfs(n+1); l[13+i]=0;r[13+i+n]=0;t[13+i-n]=0; } } } else { ans++; if(ans<=3) { for(int i=1;i<m;i++)printf("%d ",shu[i]); printf("%d\n",shu[m]); } if(ans>14200){ans=73712;return;}//12个时是14200,13个时是73712所以就减一下, } //时间上能省上一点。不过是鸡肋 } int main() { freopen("checker.in","r",stdin); freopen("checker.out","w",stdout); scanf("%d",&m); ans=0; memset(l,0,sizeof(l));memset(r,0,sizeof(r));memset(t,0,sizeof(t)); dfs(1); printf("%d\n",ans); }
USER: Neal Gavin Gavin [nealgav1] TASK: checker LANG: C++ Compiling... Compile: OK Executing... Test 1: TEST OK [0.000 secs, 3344 KB] Test 2: TEST OK [0.000 secs, 3344 KB] Test 3: TEST OK [0.000 secs, 3344 KB] Test 4: TEST OK [0.000 secs, 3344 KB] Test 5: TEST OK [0.000 secs, 3344 KB] Test 6: TEST OK [0.022 secs, 3344 KB] Test 7: TEST OK [0.130 secs, 3344 KB] Test 8: TEST OK [0.713 secs, 3344 KB] All tests OK.YOUR PROGRAM ('checker') WORKED FIRST TIME! That's fantastic -- and a rare thing. Please accept these special automated congratulations.
Here are the test data inputs:
------- test 1 ---- 6 ------- test 2 ---- 7 ------- test 3 ---- 8 ------- test 4 ---- 9 ------- test 5 ---- 10 ------- test 6 ---- 11 ------- test 7 ---- 12 ------- test 8 ---- 13Keep up the good work!
Thanks for your submission!
We use a recursive complete search to simply test all boards. The search proceeds by trying to put one checker in each row. We keep track of which columns and diagonals already have checkers on them in the "col", "updiag", and "downdiag" arrays.
Since we generate solutions in increasing order, we record the first 3 in the "sol" array.
Symmetry enables us to count the first half of the solutions double and avoid calculating the second half. An exception happens when N is odd; the odd row needs to be counted once.
The N>6 lines get the program out of trouble when N==6, because at that point, the first 3 solutions include one of the symmetric answers.
Since we number rows from 0 to N-1 rather than 1 to N, we need to add 1 to each digit as we print (in "printsol").
/* TASK: checker LANG: C */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #define MAXN 16 int n; int nsol, nprinted; char row[MAXN]; FILE *fout; void solution() { int i; for(i=0; i<n; i++) { if(i != 0) fprintf(fout, " "); fprintf(fout, "%d", row[i]+1); } fprintf(fout, "\n"); } /* Keep track of whether there is a checker on each column, and diagonal. */ char col[MAXN]; /* (i, j) -> j */ char updiag[2*MAXN]; /* (i, j) -> i+j */ char downdiag[2*MAXN]; /* (i, j) -> i-j + N */ /* * Calculate number of ways to place checkers * on each row of the board starting at row i and going to row n. */ void nway(i, lim) { int j; if(i == n) { nsol++; if (n > 6 && row[0] < n/2) nsol++; if (nprinted++ < 3) solution(); return; } for(j=0; j<lim; j++){ if(!col[j] && !updiag[i+j] && !downdiag[i-j+MAXN]){ row[i] = j; col[j]++; updiag[i+j]++; downdiag[i-j+MAXN]++; nway(i+1,n); col[j]--; updiag[i+j]--; downdiag[i-j+MAXN]--; } } } main(void) { FILE *fin = fopen("checker.in", "r"); fout = fopen("checker.out", "w"); fscanf(fin, "%d", &n); nway(0, n>6?(n+1)/2:n); fprintf(fout, "%d\n", nsol); exit (0); }
Clever Michael Rybak's Solution
The Ukraine's Michael Rybak removed the 'this row is used' search (which obviously at the end of the recursion is a lot of wasted iterating) and replaced it with a list of valid rows to use (which presumably has but a single element at the end of the recursion). His program runs almost 4x faster then N==13.
Program Checker; Var diagPLUS: Array[2..30] Of Boolean; diagMINUS: Array[-15..15] Of Boolean; sol: Array[1..15] Of ShortInt; i,n,found: Longint; stop: Boolean; next,prev: Array[0..16] Of ShortInt; sy: ShortInt; Procedure Search0(y:ShortInt); Var x,i:ShortInt; Begin If stop Then Exit; If y=n+1 Then Begin Inc(found); If found<4 Then Begin For i:=1 To n-1 Do Write(sol[i],' '); Writeln(sol[n]); End Else stop:=True; Exit; End; If sol[y]<>0 Then Begin Search0(y+1); Exit; End; x:=next[0]; While x<=n Do Begin If Not ((diagPLUS[x+y]) Or (diagMINUS[x-y])) Then Begin sol[y]:=x; diagPLUS[x+y]:=True; diagMINUS[x-y]:=True; next[prev[x]]:=next[x]; prev[next[x]]:=prev[x]; Search0(y+1); diagPLUS[x+y]:=False; diagMINUS[x-y]:=False; next[prev[x]]:=x; prev[next[x]]:=x; End; x:=next[x]; End; sol[y]:=0; End; Procedure Search; Var x:ShortInt; Begin If sy=n+1 Then Begin Inc(found); Exit; End; If sol[sy]<>0 Then Begin Inc(sy); Search; Dec(sy); Exit; End; x:=next[0]; While x<=n Do Begin If Not ((diagPLUS[x+sy]) Or (diagMINUS[x-sy])) Then Begin sol[sy]:=x; diagPLUS[x+sy]:=True; diagMINUS[x-sy]:=True; next[prev[x]]:=next[x]; prev[next[x]]:=prev[x]; Inc(sy); Search; Dec(sy); diagPLUS[x+sy]:=False; diagMINUS[x-sy]:=False; next[prev[x]]:=x; prev[next[x]]:=x; End; x:=next[x]; End; sol[sy]:=0; End; Procedure Search2(miny:Longint); Var x,y,i:ShortInt; curf:Longint; Begin x:=n Div 2+1; For y:=miny To n Div 2 Do If Not (diagPLUS[x+y] Or diagMINUS[x-y]) Then Begin curf:=found; sol[y]:=x; diagPLUS[x+y]:=True; diagMINUS[x-y]:=True; next[prev[x]]:=next[x]; prev[next[x]]:=prev[x]; sy:=1; Search; If y>miny Then found:=found+(found-curf); sol[y]:=0; diagPLUS[x+y]:=False; diagMINUS[x-y]:=False; next[prev[x]]:=x; prev[next[x]]:=x; End; End; Procedure Search1; Var x,y,i:ShortInt; Begin y:=n Div 2+1; For x:=1 To n Div 2 Do Begin sol[y]:=x; diagPLUS[x+y]:=True; diagMINUS[x-y]:=True; next[prev[x]]:=next[x]; prev[next[x]]:=prev[x]; Search2(x); diagPLUS[x+y]:=False; diagMINUS[x-y]:=False; next[prev[x]]:=x; prev[next[x]]:=x; End; sol[y]:=0; found:=found*4; x:=n Div 2+1; sol[y]:=x; diagPLUS[x+y]:=True; diagMINUS[x-y]:=True; next[prev[x]]:=next[x]; prev[next[x]]:=prev[x]; sy:=1; Search; End; Begin Assign(Input,'checker.in'); Reset(Input); Assign(Output,'checker.out'); Rewrite(Output); Read(n); found:=0; FillChar(diagPLUS,SizeOf(diagPLUS),False); FillChar(diagMINUS,SizeOf(diagMINUS),False); FillChar(sol,SizeOf(sol),0); For i:=0 To n+1 Do Begin prev[i]:=i-1; next[i]:=i+1; End; If n Mod 2=0 Then Begin stop:=False; Search0(1); sy:=1; found:=0; Search; End Else Begin stop:=False; Search0(1); found:=0; Search1; End; Writeln(found); Close(Output); End.
Clever Romanian Solution
Submitted by several from Romania, this solution uses bitmasks instead of a list to speed searching:
#include <stdio.h> #include <stdlib.h> #include <fstream.h> #define MAX_BOARDSIZE 16 typedef unsigned long SOLUTIONTYPE; #define MIN_BOARDSIZE 6 SOLUTIONTYPE g_numsolutions = 0; void Nqueen(int board_size) { int aQueenBitRes[MAX_BOARDSIZE]; /* results */ int aQueenBitCol[MAX_BOARDSIZE]; /* marks used columns */ int aQueenBitPosDiag[MAX_BOARDSIZE]; /* marks used "positive diagonals" */ int aQueenBitNegDiag[MAX_BOARDSIZE]; /* marks used "negative diagonals" */ int aStack[MAX_BOARDSIZE + 2]; /* a stack instead of recursion */ int *pnStack; int numrows = 0; /* numrows redundant - could use stack */ unsigned int lsb; /* least significant bit */ unsigned int bitfield; /* set bits denote possible queen positions */ int i; int odd = board_size & 1; /* 1 if board_size odd */ int board_m1 = board_size - 1; /* board size - 1 */ int mask = (1 << board_size) - 1; /* N bit mask of all 1's */ aStack[0] = -1; /* sentinel signifies end of stack */ for (i = 0; i < (1 + odd); ++i) { bitfield = 0; if (0 == i) { int half = board_size>>1; /* divide by two */ bitfield = (1 << half) - 1; pnStack = aStack + 1; /* stack pointer */ aQueenBitRes[0] = 0; aQueenBitCol[0] = aQueenBitPosDiag[0] = aQueenBitNegDiag[0] = 0; } else { bitfield = 1 << (board_size >> 1); numrows = 1; /* prob. already 0 */ aQueenBitRes[0] = bitfield; aQueenBitCol[0] = aQueenBitPosDiag[0] = aQueenBitNegDiag[0] = 0; aQueenBitCol[1] = bitfield; aQueenBitNegDiag[1] = (bitfield >> 1); aQueenBitPosDiag[1] = (bitfield << 1); pnStack = aStack + 1; /* stack pointer */ *pnStack++ = 0; /* row done -- only 1 element & we've done it */ bitfield = (bitfield - 1) >> 1; /* bitfield -1 is all 1's to the left of the single 1 */ } for (;;) { lsb = -((signed)bitfield) & bitfield; /* this assumes a 2's complement architecture */ if (0 == bitfield) { bitfield = *--pnStack; /* get prev. bitfield from stack */ if (pnStack == aStack) /* if sentinel hit.... */ break; --numrows; continue; } bitfield &= ~lsb; /* bit off -> don't try it again */ aQueenBitRes[numrows] = lsb; /* save the result */ if (numrows < board_m1) { /* more rows to process? */ int n = numrows++; aQueenBitCol[numrows] = aQueenBitCol[n] | lsb; aQueenBitNegDiag[numrows] = (aQueenBitNegDiag[n] | lsb) >> 1; aQueenBitPosDiag[numrows] = (aQueenBitPosDiag[n] | lsb) << 1; *pnStack++ = bitfield; bitfield = mask & ~(aQueenBitCol[numrows] | aQueenBitNegDiag[numrows] | aQueenBitPosDiag[numrows]); continue; } else { ++g_numsolutions; bitfield = *--pnStack; --numrows; continue; } } } g_numsolutions *= 2; } int main(int argc, char** argv) { ifstream f("checker.in"); ofstream g("checker.out"); long boardsize,s[20],ok,k,i,sol=0; f>>boardsize; Nqueen (boardsize); k=1; s[k]=0; while (k>0) { ok=0; while(s[k]<boardsize && !ok) { ok=1; s[k]++; for(i=1;i<k;i++) if(s[i]==s[k] || abs(s[k]-s[i])==abs(k-i)) ok=0; } if(sol!=3) if(!ok) k--; else if(k==boardsize) { for(i=1;i<boardsize;i++) { for(int j=1;j<=boardsize;j++) if(s[i]==j) g<<j<<" "; } for(i=1;i<=boardsize;i++) if(s[boardsize]==i) g<<i; g<<"\n"; sol++; } else { k++; s[k]=0; } else break; } g<<g_numsolutions<<"\n"; f.close(); g.close(); return 0; }