/* Knight's Tour Problem - a kind of undirected Hamiltonian circuits Using an advanced algorithm - at least 10000 times faster than simple backtracking The key idea is to detect bi-degree nodes and also the art of programming Enjoy it! Author : Fei Lu Email : flylandcs@hotmail.com */ #define true 7361 #define false 28456 #define N 8 #define INTERVAL 1 // Display the result on every 100 solutions #define Magic_SUM 260 #include #include #include #include #include // Optimized for 8x8 board #define ROW(x) (x>>3) #define COL(x) (x&7) //#define ROW(x) (x/N) //#define COL(x) (x%N) double duration; unsigned int nSolution=0; // delta-i, delta-j for 8 neighbourhood int iDir[8]={-2,-1, 1, 2, 2, 1, -1, -2}; int jDir[8]={1, 2, 2, 1, -1, -2, -2, -1}; /* At each position (i,j), the knight has t directions to go Define: nBranch[here]=t, (t<=8) ; "here" is encoded as i*N+j, (i,j: 0 ... N-1) Given the current position: here and the k(th) direction, which the knight chooses to move, Define: branch[here][k] = the next position The problem is now abstracted as a graph problem. There are N^2 nodes and their connections are stored in the array called "branch" */ int branch[N*N][8], nBranch[N*N]; // ~~~~~~ states involved in path searching ~~~~~~ int board[N*N]; // board[v] = t, indicating node "v" is visited at the t(th) step, (t: 1 ... N*N), // board[v] = 0 means this node has not been visited yet. int degree[N*N];// degree[v] is the number of non-visited neighbours of node v // ****** ASSERTION ******** // A. If a non-visited node has a degree of two, then one is for in-degree, one is for out-degree. // B. If the knight is to arrive on node v: // 1. If a neighbour has a degree of 1, it must be the endpoint // 2. If the "endpoint" is already defined, // there can exist ONLY one neighbour whose degree is 2. // This neighbour must be visited immediatelly after node v. // 3. If the "endpoint" is not defined, // there can exist AT MOST two neighbours whose degree is 2. // One of this neighbour must be defined as endpoint // The other must be visited immediatelly after node v. // // "neighbours" mentioned in the above assertions refers to // non-visited neighbours int nBranch_to_go[N*N]; // nBranch_to_go[here]: the number of non-visited neighbours int branch_to_go[N*N][8]; // branch_to_go[here]: array to store all non-visited neighbours int endpoint_set[N*N][8]; // endpoint_set[here][d]=true, if visiting branch_to_go[here][d] // will cause an endpoint to appear somewhere else int path[N*N+1];// path[k] = v, where v is the k(th) visited node. Note: v can be decoded into (i,j), v=i*N+j int dir[N*N+1]; // dir[k] = t, where the knight at k(th) step is searching the t(th) edge int n; // the current step. n: 1 ... N*N int endpoint_Defined; // If there exist one non-visited node whose degree is one, then it is defined as the endpoint int rsum[N],csum[N]; // sum of each row/column int cn[N],rn[N]; // number of empty holes left in each row/coloum int up[N+1]; // up[i] = N*N + ... + N*N-i+1 int down[N*N+2][N+1]; // down[n][i] = n + ... + (n-i+1) // Syntactic sugars. Do not use the following arrays in time-critical functions. int (*board2D)[N]=(int(*)[N])&board; // board2D[N][N] int (*degree2D)[N]=(int(*)[N])°ree; // degree2D[N][N] void printBoard() { int i,j; char buf[80]; for(i=0; i<4*N+1; i++) buf[i]='-'; buf[i]=0; printf("%s\n",buf); for(i=0; i0) continue; if(degree[u]==2){ if(degree2>=atmost){ rollback_degree(v,count); //Check failed return false; }; u2[degree2++]=u; u2index=count; }; degree[u]--; // If v is visited, neighbours' degree must be decreased branch_to_go[v][count++]=u; }; ///////////////////////////////////////////////////////// nBranch_to_go[v]=count; memset(endpoint_set[v],0,sizeof(int)*8); if(degree2==2){ // Case 1: TWO degree-of-two nodes and endpoint_Defined // assert: endpoint_Defined==true nBranch_to_go[v]=2; branch_to_go[v][0]=u2[0]; branch_to_go[v][1]=u2[1]; endpoint_set[v][0]=true; endpoint_set[v][1]=true; }else if(degree2==1){ if(endpoint_Defined){ // Case 2: only ONE degree-of-two node and endpoint_Defined nBranch_to_go[v]=1; branch_to_go[v][0]=u2[0]; }else{ // Case 3: only ONE degree-of-two and NO endpoint_Defined for(i=0; i0) continue; degree[u]++; // restore the degree if u is not visited }; } // Precondition: label (n+1) is going to be placed at node "there" // Postcondition: No side-effect if check fails // rsum,csum,rn,cn are changed if check succeeds inline int magic_check(int there) { int s1,s2; int r,c; r=ROW(there); c=COL(there); s1=rsum[r] + (n+1); if( s1 + up[rn[r]-1] < Magic_SUM ) return false; if( s1 + down[n+2][rn[r]-1] > Magic_SUM ) return false; s2=csum[c] + (n+1); if( s2 + up[cn[c]-1] < Magic_SUM ) return false; if( s2 + down[n+2][cn[c]-1] > Magic_SUM ) return false; rsum[r] = s1; csum[c] = s2; rn[r]--; cn[c]--; return true; } inline void retreat(int here) { rollback_magic(here,n); restore_degree(here); board[here]=0; int v = path[n-1]; int d = dir[n-1]; if(endpoint_set[v][d]==true){ endpoint_Defined=false; }; n--; } /* Depth-First-Search */ void Travel() { int m,d; int here,there; int rollback_endpoint_Defined; // Used for rollback while(n>=1){ LOOP: /*if(board2D[0][0]==1 && board2D[1][2]==2 && board2D[2][0]==3)/* && board2D[4][1]==4)/* && board2D[3][3]==5/* && board2D[5][4]==6 && board2D[3][5]==7 )/*&& board2D[0][1]==10)/* && board2D[0][2]==35)/* && board2D[0][3]==28 && board2D[0][4]==25)/* && board2D[0][5]==12)*/ //if(board2D[0][4]==25) //if(board2D[4][3]==18) //if(board2D[4][4]==23) /*if(board2D[4][0]==21 && board2D[3][0]==30) { printBoard(); //printDegree(); for(int i=0; i<6; i++) printf("%d ",rsum[i]); printf("********\n"); //getch(); }; printBoard(); for(int i=0; i<6; i++) printf("%d ",rsum[i]); printf("********\n"); getch(); /* if(n==7){ printBoard(); for(int i=0; i<6; i++) printf("%d ",rsum[i]); printf("********\n"); } if(n==23){ printBoard(); for(int i=0; i<6; i++) printf("%d ",rsum[i]); printf("********\n"); } */ // if(n<34) return; here=path[n]; m=nBranch_to_go[here]; for(d=++dir[n]; dN*N-i; j--) sum+=j; up[i]=sum; } for(n=1; n<=N*N+1; n++){ for(i=0; i<=N; i++){ sum=0; for(j=0; j=N || jp>=N) continue; branch[i*N+j][count]=ip*N+jp; count++; }; degree[i*N+j]=nBranch[i*N+j]=count; }; }; endpoint_Defined = false; n=0; } /* Force the knight to go to the next position(r,c), can be used in testing */ void go(int r,int c) { int there=r*N+c; int i,a1,a2; path[n+1]=there; board[there]=n+1; if(n>=1){ int v = path[n]; int m = nBranch_to_go[v]; for(i=0; i