n-queens problem

Let's talk about algorithms!

Moderator: Board moderators

agolsme
New poster
Posts: 6
Joined: Tue Mar 08, 2005 5:22 am

n-queens problem

Post by agolsme »

This is an implementation of the n-queens problem(putting a queen on each row/column such that no queen attacks another)

The code works fine, however, i am unable to optimize it such that i'll solve case n=13 in under a second.

Perhaps there's a different algorithm I could use? Or, some one to optimize the recursion? gprof says that ~40% of the time is being used in the function canPlace() and ~40-50% in solve2()

Code: Select all

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
//the board representation
static unsigned int rows[14] = {0};
static unsigned int diagonal[29] = {0};
static unsigned int odiagonal[29] = {0};

static unsigned int board[14] = {0};
static unsigned int solutions[3][14];
static unsigned int count = 0;
static unsigned int total=0;

inline void mark(unsigned int col,unsigned int i)
{
        rows[i] = 1;
        diagonal[total + col - i] = 1;
       odiagonal[col + i - 1] = 1;
}
inline void clear(unsigned int col,unsigned int i)
{
    rows[i] = 0;
        diagonal[total + col - i] = 0;
        odiagonal[col + i - 1] =0;
}
inline void print()
{
        memmove(solutions[count], board, sizeof(int) * (total + 1));
}
inline bool canPlace(unsigned int column, unsigned int i)
{
    return (rows[i] == 0 && diagonal[total + column - i] == 0 && odiagonal[column + i -1] == 0);
}
    
inline void solve2(unsigned int column)
{
    unsigned int i;
    for(i  = 1; i <= total; ++i)
    {
        if(canPlace(column, i))
        {
            if(column == total)
            {
                board[column] = i;
                if(count <3)   
                {
                    print();
                }
                ++count;
                board[column]=0;
            }
            else
            {
                board[column] = i;
                mark(column, i);
                solve2(column  + 1);
                clear(column, i);
                board[column] = 0;
            }   
        }
    }
}
            
inline void outputSolutions()
{
    unsigned int i,j;
    FILE* fout = fopen("checker.out", "w+");
    for(i = 0; i < 3; ++i)
    {
        for(j = 1; j <= total; ++j)
        {
            if(j != total)
                fprintf(fout, "%d ", solutions[i][j]);
            else
                fprintf(fout, "%d", solutions[i][j]);
        }
        fprintf(fout, "\n");
    }
    fprintf(fout, "%d\n", count);
}
int main()
{
    FILE* blah = fopen("checker.in", "r");
    unsigned int d;
    fscanf(blah, "%d", &d);
    total = d;
    solve2(1);
    outputSolutions();
}

Cosmin.ro
Learning poster
Posts: 95
Joined: Thu Aug 21, 2003 12:02 am

Post by Cosmin.ro »

As far as I know in USACO the time limit is 5 seconds, you don't really need it to get in under 1 second.
agolsme
New poster
Posts: 6
Joined: Tue Mar 08, 2005 5:22 am

Post by agolsme »

well, either way. I am not passing the time limits. Any idea how i could make this faster?
Cosmin.ro
Learning poster
Posts: 95
Joined: Thu Aug 21, 2003 12:02 am

Post by Cosmin.ro »

You can speed this up by observing that some configurations are just like others when they are reflected, so if you try to only generate a configuration once and counting it twice then you get an algo twice as fast, but your algo is ok for passing the USACO dataset ... maybe you are interested in test 13 in particular becose it gets you on the leaderboard ....
agolsme
New poster
Posts: 6
Joined: Tue Mar 08, 2005 5:22 am

Post by agolsme »

that's not it though. I've run this through test 12 something like 20 times. Yet I am still not passing the judge on USACO for the entire thing.

I don't really care about getting on the leaderboard(not nearly as much as getting past this program anyways)

Also, the time limit on USACO is 1 second. I just checked, as my program runs at about 1.71 seconds and it does not pass.
Cosmin.ro
Learning poster
Posts: 95
Joined: Thu Aug 21, 2003 12:02 am

Post by Cosmin.ro »

You can try to place the first queen in the first n/2 colums and multiply the number you get by 2, and then add it with the number of the configurations for wich the first queen is in the n/2+1 th colum, this simple optimisation cuts your time in half. Also why do you save the solutions you just need their number ...
agolsme
New poster
Posts: 6
Joined: Tue Mar 08, 2005 5:22 am

Post by agolsme »

I only save the first 3, which is required.

I changed it so that it only processes half, but I am still not reaching the 1 second mark.

any idea how i can further improve speed?

Code: Select all

#include<stdio.h>
//the board representation
static unsigned int rows[14] = {0};
static unsigned int diagonal[29] = {0};
static unsigned int odiagonal[29] = {0};
static unsigned int board[14] = {0};
static unsigned int total=0;
static unsigned int count =0;
static unsigned int blahcount = 0;
static FILE* fout;
inline void mark(unsigned int col, unsigned int i)
{
        rows[i] = 1;
        diagonal[total + col - i] = 1;
        odiagonal[col + i - 1] = 1;
}
inline void clear(unsigned int col, unsigned int i)
{
        rows[i] = 0;
        diagonal[total + col - i] = 0;
        odiagonal[col + i - 1] =0;
}
inline void print()
{
    int j;
    for(j = 1; j<=total; j++)
    {
        if(j == total)
            fprintf(fout, "%d", board[j]);
        else
            fprintf(fout, "%d ", board[j]);
    }
    fprintf(fout, "\n");
}
inline bool canPlace(unsigned int column, unsigned int i)
{
    return (rows[i] == 0 && diagonal[total + column - i] == 0 && odiagonal[column + i -1] == 0);
}
    
inline void solve2(unsigned int column, unsigned int val = total)
{
    int i;
    for(i = 1; i <= val; ++i)
    {
        if(canPlace(column,i))
        {
            if(column == total)
            {
                board[column] = i;
                if(count <3)   
                {
                    print();
                }
                ++count;
                board[column]=0;
            }
            else
            {
                board[column] = i;
                mark(column,i);
                solve2(column  + 1);
                clear(column,i);
                board[column] = 0;
            }   
        }
    }
}

inline void solve()
{
    int p;
    bool isOdd;
    if(true == (isOdd =total % 2))
        blahcount = total / 2 + 1;
    else
        blahcount = total / 2;
    for(p = 1; p <= blahcount; ++p)
    {
        board[1] = p;
        mark(1,p);
        if(isOdd && p == blahcount)
        {
            solve2(2, blahcount + 1);
        }
        else
            solve2(2);
        clear(1,p);
    }
}
            
int main()
{
    FILE* blah = fopen("checker.in", "r");
    fout = fopen("checker.out", "w+");
    unsigned int d;
    fscanf(blah, "%d", &d);
    fclose(blah);
    total = d;
    if(total < 7)
    {
        solve2(1);
        fprintf(fout, "%d\n", count);
    }
    else
    {
        solve();
        fprintf(fout, "%d\n", 2*count);
    }
}
midra
Experienced poster
Posts: 119
Joined: Fri Feb 13, 2004 7:20 am
Contact:

Post by midra »

Hi!
I am stuck in the same problem in the same test case and with the time limit too!
The time limit for this problem now is 1 second
My code get 1.03 seconds... I don't know how to make it faster, I make all I know, including what I read in this post

here is my code:

Code: Select all

#include <stdio.h>

FILE *in,*out;
enum bool {FALSE,TRUE};
typedef enum bool boolean;
boolean row[15],diag1[28],diag2[28],tmp;
int ind,n,p,table[4][15],h;

init(){
	int i,j=2*n;
	for(i=1;i<h;i++)row[i]=TRUE;
	for(i=1;i<j;i++) diag1[i]=TRUE;
	for(i=1;i<j;i++) diag2[i]=TRUE;
}

search(int i,boolean *s,int col[]){
	int j=1,k;
	*s=FALSE;
	while(j<h){
		if((row[j]) && (diag1[i+j-1]) && (diag2[n-i+j])){
			row[j]=diag1[i+j-1]=diag2[n-i+j]=FALSE;
			col[i]=j;
			if(i<n){
				search(i+1,s,col);
      			if(!*s)
					row[j]=diag1[i+j-1]=diag2[n-i+j]=TRUE;
			    if(i==1 && j==p){
					if (tmp==FALSE){
						if(p==n/2){
							p++;
							ind*=2;
						}
						else print_res(ind);
					}  	
			       else if(tmp==TRUE && n!=6)
				     print_res(ind*2);
				}
			}
			else{
				row[j]=diag1[i+j-1]=diag2[n-i+j]=TRUE;
				if(ind<3){
				  for(k=1;k<h;k++)
				    table[ind][k]=col[k];
 				}
			   	ind++;
			   *s=FALSE;
		   }
		}
    	j++;
	}
}

print_res(int res){
   int i,j;
   out=fopen("checker.out","w");
   for(j=0;j<3;j++){
	 for(i=1;i<n;i++)
       fprintf(out,"%d ",table[j][i]);
     fprintf(out,"%d\n",table[j][n]);  
   }
   fprintf(out,"%d\n",res);
   exit(0);
}

int main()
{
  int col[15],i,j;boolean s;
  in=fopen("checker.in","r");
  out=fopen("checker.out","w");
  fscanf(in,"%d",&n);
	 ind=0;p=n/2;h=n+1;
	 if(n%2==0) tmp=TRUE;
	 else tmp=FALSE;
	 init();
     search(1,&s,col);
	 for(i=0;i<3;i++){
	   for(j=1;j<n;j++)
         fprintf(out,"%d ",table[i][j]);
       fprintf(out,"%d\n",table[i][n]);  
     }
	 fprintf(out,"%d\n",ind);
  exit(0);
}
and here the result of the USACO judge
Executing...
Test 1 OK [0 secs]
Test 2 OK [0 secs]
Test 3 OK [0 secs]
Test 4 OK [0 secs]
Test 5 OK [0 secs]
Test 6 OK [0.03 secs]
Test 7 OK [0.17 secs]
Execution error: Your program (`checker') used more than the allotted
runtime of 1 seconds (it ended or was stopped at 1.03 seconds) when
presented with test case 8, shown below.
----- Test Case 8 ------
13
I don't know what to do...there are just 0.03 seconds to get ac
if someone knows.....plz help! and thanks!
byee
Cosmin.ro
Learning poster
Posts: 95
Joined: Thu Aug 21, 2003 12:02 am

Post by Cosmin.ro »

Code: Select all

TASK: checker
LANG: PASCAL

Compiling...
Compile: OK

Executing...
Test 1 OK [0 secs]
Test 2 OK [0 secs]
Test 3 OK [0 secs]
Test 4 OK [0.01 secs]
Test 5 OK [0.01 secs]
Test 6 OK [0.03 secs]
Test 7 OK [0.16 secs]
Test 8 OK [0.96 secs]
And this is my source:

Code: Select all


var n:longint;
    fis:text;
    nr:longint;
    sol:array[1..13] of longint;
    a:array[1..3,1..13] of longint;
    l:array[1..13] of longint;
    d:array[1..26] of longint;
    d1:array[1..26] of longint;

procedure back1(x:longint);
var i:longint;
begin
  if nr=3 then exit;
  if x=n+1 then begin
    inc(nr);
    for i:=1 to n do a[nr][i]:=sol[i];
    if nr=3 then exit;
  end else begin
      for i:=1 to n do
        if (l[i]=0) and (d[i+x]=0) and (d1[13 + i - x]=0) then begin
          sol[x]:=i;
          l[i]:=1;
          d[i + x]:=1;
          d1[13 + i - x]:=1;
          back1(x + 1);
          l[i]:=0;
          d[i + x]:=0;
          d1[13 + i - x]:=0;
        end;
  end;
end;

procedure back(x:longint);
var i,j,k:longint;
begin
  if x=n then begin
     i:=1;
     j:=x;
     k:=13-x;
     repeat
       inc(j);
       inc(k);
       if (l[i]=0) and (d[j]=0) and (d1[k]=0) then begin
          inc(nr);
          break;
       end;
       inc(i);
     until i>n;
  end else begin
      i:=1;
      j:=x;
      k:=13-x;
      repeat
        inc(j);
        inc(k);
        if (l[i]=0) and (d[j]=0) and (d1[k]=0) then begin
          l[i]:=1;
          d[j]:=1;
          d1[k]:=1;
          back(x+1);
          l[i]:=0;
          d[j]:=0;
          d1[k]:=0;
        end;
        inc(i);
      until i>n;
  end;
end;

var i,j:longint;

begin
  assign(fis,'checker.in');reset(fis);
  readln(fis,n);
  close(fis);
  nr:=0;
  fillchar(sol,sizeof(sol),0);
  fillchar(l,sizeof(l),0);
  fillchar(a,sizeof(a),0);
  fillchar(d,sizeof(d),0);
  fillchar(d1,sizeof(d1),0);
  back1(1);
  nr:=0;
  fillchar(sol,sizeof(sol),0);
  fillchar(l,sizeof(l),0);
  fillchar(d,sizeof(d),0);
  fillchar(d1,sizeof(d1),0);
  for i:=1 to n div 2 do
     if (l[i]=0) and (d[i+1]=0) and (d1[i-1]=0) then begin
         l[i]:=1;
         d[i+1]:=1;
         d1[13+i-1]:=1;
         back(2);
         l[i]:=0;
         d[i+1]:=0;
         d1[13+i-1]:=0;
  end;

  nr:=2*nr;

  fillchar(sol,sizeof(sol),0);
  fillchar(l,sizeof(l),0);
  fillchar(d,sizeof(d),0);
  fillchar(d1,sizeof(d1),0);

  if odd(n) then begin
     i:=n div 2 + 1;
     l[i]:=1;
     d[i+1]:=1;
     d1[13+i-1]:=1;
     back(2);
     l[i]:=0;
     d[i+1]:=0;
     d1[13+i-1]:=0;
  end;

  assign(fis,'checker.out');rewrite(fis);
  for i:=1 to 3 do begin
    write(fis,a[i][1]);
    for j:=2 to n do write(fis,' ',a[i][j]);
    writeln(fis);
  end;
  writeln(fis,nr);
  close(fis);

end.
This is messy and barely gets in the timelimit ... I don't know what to optimise in this solution. If you want to get a faster sol you could read about Knuth's dancing links tehnique in backtracking solutions: http://www-cs-faculty.stanford.edu/~knu ... rints.html (dancing links paper), I'm lazy to do it myself.
agolsme
New poster
Posts: 6
Joined: Tue Mar 08, 2005 5:22 am

Post by agolsme »

I actually ended up figuring this solution out. There was nothing wrong with simple reflection, as it turns out, I only needed to condense my C a little: use static variables, reduce function calls, etc.
User avatar
D3adly
New poster
Posts: 2
Joined: Mon Apr 18, 2005 2:29 pm
Contact:

Post by D3adly »

Hello! I'm new here, and I need help! Why I get this:

Code: Select all

TASK: checker
LANG: C

Compiling...
Compile: OK

Executing...
Test 1 OK [0 secs]
Test 2 OK [0 secs]
Test 3 OK [0.01 secs]
Test 4 OK [0.01 secs]
Test 5 OK [0.04 secs]
Test 6 OK [0.26 secs]
Execution error on machine 192.65.171.166: Your program (`checker')
exited with signal #11 (segmentation violation [maybe caused by
accessing memory out of bounds, array indexing out of bounds, using a
bad pointer (failed open(), failed malloc), or going over the maximum
specified memory limit]) when presented with test case 7, shown below.
The program ran for 0.69 CPU seconds before the signal. 

----- Test Case 7 ------
12
----------------------------
... with my code:


Code: Select all

#include <stdio.h>

int matrica[40][40];
int rj[100][40],buff[40];
int x,y,brojac,br,n,rjb=0,stop,stanje,s,poslije=0,t;

int search(int a);

int isattack(int c, int d); 



int main (void){
int k, j;
FILE *fin=fopen("checker.in","r");
FILE *fout=fopen("checker.out","w");
memset(rj,0,sizeof (rj));
brojac=0;
fscanf (fin,"%d",&n);
x=0;
y=0;
s=0;
for (k=0;k<=n;++k){
 buff[k]=-1;
}

search(0);


for (k=0;k<brojac;++k) {
 for (j=0;j<n;++j){
if (j!=n-1){
  fprintf (fout,"%d ",rj[k][j]+1);
 }
 else{
  fprintf (fout,"%d",rj[k][j]+1);
 }
 }
 fprintf (fout,"\n");
}

if (stanje==10) {
if (n%2==0) rjb*=2;
else
rjb+=s;
}
fprintf (fout,"%d\n",rjb);
fclose(fin);
fclose(fout);
 return 0;
}




//search function
int search(int a){
int k,j;
a=x;
br=0;

if (n>6 && buff[0]>=n/2+n%2){
 stanje=10;
 return 0;
}

if (buff[0]==n/2-1 && n%2!=0) s=rjb;

if (x==n){
++rjb;
if (brojac<3){
for (k=0;k<n;++k){
 rj[brojac][k]=buff[k];
}
++brojac;
}
return 0;
}
for (k=0;k<n;++k){
if (k!=buff[x] && k>buff[x]){
 if (isattack(x,k)==0){
  matrica[x][k]=1;
  buff[x]=k;
  br=10;
  search(++x);
 }
 }
}
  if (br==0){
   if (x>=1){
 --x;
 matrica[x][buff[x]]=0;
  for (k=x+1;k<n;++k) buff[k]=-1;
 search(x-1);
  }
 }
}



//isattack function
int isattack(int c,int d){
int k;
x=c;
y=d;
 if (matrica[x-1][y+1]==1 || matrica[x-1][y-1]==1) return 1;
 for (k=0;k<x;++k){
  if (buff[k]==y) return 1;
 }
 for (k=0;k<n;++k){  
 if (matrica[k+x][k+y]==1) return 1;
 if (matrica[x+k][y-k]==1 && x+k<n) return 1;
 if (x-k>=0 && y-k>=0 && matrica[x-k][y-k]==1){
  return 1;
 }
 if (matrica[x-k][y+k]==1 && y+k<n) {
  return 1;
 }
 }
 return 0;
}


P.S. My english it's not so well, but you will understand me.
nibbler
New poster
Posts: 22
Joined: Fri Jun 04, 2004 10:30 pm

Post by nibbler »

search() calls itself several thousand times and you run out off stack, I haven't studied why. pozdrav ;)
User avatar
D3adly
New poster
Posts: 2
Joined: Mon Apr 18, 2005 2:29 pm
Contact:

Post by D3adly »

Yes, I know that, but I don't know when to stop it. :-?

P.S. pozdrav i tebi
Adrian Kuegel
Guru
Posts: 724
Joined: Wed Dec 19, 2001 2:00 am
Location: Germany

Post by Adrian Kuegel »

In your isattack() function you don't check if you are inside the bounds of the matrix array. Btw, this function is too slow to pass bigger tests; you need a isattack() test that runs in constant time.
Mohammad Mahmudur Rahman
Experienced poster
Posts: 154
Joined: Sat Apr 17, 2004 9:34 am
Location: EEE, BUET

Post by Mohammad Mahmudur Rahman »

This problem gave me a lot of trouble too, once the time limit was reduced from 5s to 1s. However, I managed to pass the 8th test case (n = 13) within time using the following 2 tricks -

1. Each cell [r,c] can be represented using 3 values - one for the row (or column) number, one for the main diagonal number & the remaining for the inverse diagonal number. This will ensure a first isattack() function.

2. You only need to place pieces in the first half of the board for the first column. Due to symmetry of the board, you can multiply this value by 2 to get the answer. Don't forget to handle the middle row manually when n is odd.

However, I wonder how can some people pass the case n = 13 in just 0 secs. :o Is there really such a good algorithm or should I believe that they have pre-calculated & printed the answer for n = 13 ? :roll:
You should never take more than you give in the circle of life.
Post Reply

Return to “Algorithms”