USACO section 1.5.4 Checker Challenge

1. 第一次做位运算的题,参考了这段经典代码(n皇后问题):

void Queen(int row, int ld, int rd)
{
    int pos, p;
    int upperlim = (1 << n) - 1;
    if (row != upperlim)
    {
        pos = upperlim & ~(row | ld | rd);
        while (pos != 0)
        {
            p = pos & -pos;  // v & -v 的作用就是取出右起连续的 0 以及首次出现的 1
            pos -= p;
            Queen(row + p, (ld + p) << 1, (rd + p) >> 1);
        }
    }
    else ++sum;
}

2. 以下是代码:

/*
ID: dollar4
PROG: checker
LANG: C++
*/
#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
#include <cstring>
#include <iomanip>
#include <cmath>

using namespace std;
ofstream fout ("checker.out");
ifstream fin ("checker.in");
int n;
int step[50];
int maxx;
int cnt = 0;
void dfs(long long row, long long ld, long long rd, int depth)
{
    if (depth == n)
    {
        cnt++;
        if (cnt <= 3)
        {
            for (int i = 0; i < depth - 1; i++)
                fout << step[i] << ' ';
            fout << step[depth - 1] << endl;
        }
        return;
    }
    long int pos, p;
    pos = (row | ld | rd) & maxx;
    while (pos != maxx)
    {
        p = (~pos) & (pos + 1) & maxx;
        if (cnt <= 2)
            step[depth] = (int)log2((double)p) + 1;
        dfs(row + p, (ld + p) << 1, (rd + p) >> 1, depth + 1);
        pos = (pos| p) & maxx;
    }
}
int main()
{
    fin >> n;
    maxx = (1 << n) - 1;
    dfs(0, 0, 0, 0);
    fout << cnt << endl;
    return 0;
}
/*
void Queen(int row, int ld, int rd)
{
    int pos, p;
    int upperlim = (1 << n) - 1;
    if (row != upperlim)
    {
        pos = upperlim & ~(row | ld | rd);
        while (pos != 0)
        {
            p = pos & -pos;  // v & -v 的作用就是取出右起连续的 0 以及首次出现的 1
            pos -= p;
            Queen(row + p, (ld + p) <> 1);
        }
    }
    else ++sum;
}
*/

3. 官方参考代码:

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


原文地址:https://www.cnblogs.com/dollarzhaole/p/3188908.html