USACO Checker Challenge

1、这道题我又SB了,N=11的时候怎么都输不出正确解,原因在于我算主对角线的时候只是将横纵相减,而忘了加n。。。注意数组的下标只能是非负数!不知道6到10的数据是怎么过的。。。

2、修改后,顺利AC~网上说用位运算什么的,不然要超时,我怎么没感觉。。。最后一个数据0.6s左右过的。。。

3、终于做完第一章了,不容易啊~对大牛来说很容易,可我是蒟蒻啊。。。

/*
ID:mrxy564
PROG:checker
LANG:C++
*/

#include<cstdio>
#include<cstring>
using namespace std;
int row[15];
bool col[15];
bool dia1[30],dia2[30];
int cnt=0;
void print(int n){
 printf("%d",row[1]);
    for(int i=2;i<=n;i++)
  printf(" %d",row[i]);
 printf("\n");
 return;
}
void dfs(int now,int n){
 if(now>n){
  cnt++;
     if(cnt<=3) print(n);
  return;
 }
    for(int i=1;i<=n;i++)
  if(!col[i]&&!dia1[now-i+n]&&!dia2[now+i]){
     row[now]=i;
     col[i]=true;
     dia1[now-i+n]=true;
     dia2[now+i]=true;
     dfs(now+1,n);
     dia2[now+i]=false;
     dia1[now-i+n]=false;
     col[i]=false;
  }
    return;
}
void init(){
    memset(col,false,sizeof(col));
 memset(dia1,false,sizeof(dia1));
 memset(dia2,false,sizeof(dia2));
}
int main(){
   freopen("checker.in","r",stdin);
   freopen("checker.out","w",stdout);
   int n;
   scanf("%d",&n);
   init();
   dfs(1,n);
   if(cnt>3) printf("%d\n",cnt);
   return 0;
}

官方题解:

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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值