0

Programming Problem Description

The n-queens puzzle is the problem of placing n queens on an n x n chessboard such that no two queens attack each other. Given an integer n, return the number of distinct solutions to the n-queens puzzle. The 1 ≤ n ≤ 9 are the constraints of Test Cases.

[Taken from here]


My Attempt

I tried to solve the problem using bit-masking. In short, we try all possible combination, and backtrack whenever solution is not possible.

We place queen row-by-row, and with each placement, we restrict some positions/box where remaining queens cannot be placed.

Now, we can identify each column by it's index, diagonal have property of same row index - column index, and anti-diagonal have property of same row index + column index.

Thus, after placing queen at any box, we will restrict columns, diagonals and anti-diagonals identified by that box. This will be done by having a bit-mask variable for all three parameters.

Here is the code in c for the same (which was submitted on Online Judge).

int N;
int count=0;

void rowExpansion(int r, int cols, int diags, int aDiags);

int totalNQueens(int n)
{    
    N=n;
    rowExpansion(0,0,0,0);
    
    return count;
}

void rowExpansion(int r, int cols, int diags, int aDiags)
{   
    if (r<N)
    {
        for (register int c=0; c<N; c++)
        {
            // current Diagonal, current antidiagonal
            int cD = r - c + N, cAd= r + c;
            
            /* Check if (r,c) is valid, Checking ith bits of Three Bit Masks.  
            If any of them is set, don't include this (r,c) */

            if ((cols & (1 << c)) || (diags & (1 << cD)) || (aDiags & (1 << cAd))) 
            continue;
            
            //Next Row traversal with updated bit-masks
            rowExpansion(r+1, cols | 1<<c, diags | 1<<cD, aDiags | 1<<cAd);
        }
    }
    
    else count++;
}

This code was working fine on Console, but on submitting it produced error. For n=1, code was giving correct output in Console, but giving wrong answer on submitting. I tried to code same technique in python and it worked fine.

Attached is the screenshot of error.

Screenshot

Here is the same code with added main and other functionalities to make it reprex, it gave correct output on CodeBlocks.

#include <stdio.h>

int N;
int count=0;

void rowExpansion(int r, int cols, int diags, int aDiags);

int totalNQueens(int n)
{
    N=n;
    rowExpansion(0,0,0,0);

    return count;
}

void rowExpansion(int r, int cols, int diags, int aDiags)
{
    if (r<N)
    {
        for (register int c=0; c<N; c++)
        {
            // current Diagonal, current antidiagonal
            int cD = r - c + N, cAd= r + c;

            /* Check if (r,c) is valid, Checking ith bits of Three Bit Masks.
            If any of them is set, don't include this (r,c) */

            if ((cols & (1 << c)) || (diags & (1 << cD)) || (aDiags & (1 << cAd))) 
            continue;

            //Next Row traversal with updated bit-masks
            rowExpansion(r+1, cols | 1<<c, diags | 1<<cD, aDiags | 1<<cAd);
        }
    }

    else count++;
}

void main()
{
    int n;
    printf("Enter Number of Queens (1-9) : ");
    scanf("%d",&n);

    if (n<1 || n>9)
    {
        printf("Wrong Input!");
    }
    else
    {
        int D[] = {0, 1, 0, 0, 2, 10, 4, 40, 92, 352};
        int x = totalNQueens(n);

        printf("Desired Output : %d\nGiven Output   : %d\n", D[n],x);
    }
}

As background, I use to practice mostly in python and not very much skilled in c programming.


Doubt(s)

  1. What is the error in this code? Is the error logical, syntactical or runtime-error?
  2. Why it happens that same code on console is successful, but fails on submitting? Can someone provide good reference for the same?
  3. Users in comments blamed Global Variables for the error. Can someone throw more light on why this happen, and provide hint on how to get rid of global variables from this code?

user438383
  • 5,716
  • 8
  • 28
  • 43
Rohit Singh
  • 156
  • 1
  • 12
  • Related? https://oeis.org/A000170 – pmg Jun 08 '22 at 12:09
  • Link is totally related, @pmg. But question is more about detecting error, and less about N-queens. I am particularly not interested in *number of distinct solution* but interested in why my code is throwing error. – Rohit Singh Jun 08 '22 at 12:13
  • Maybe check your local runs with the numbers from OEIS. If the results from your local run and the result from the online site differ, (at least) one of the results is wrong (assuming, of course, that OEIS has correct results). Usually, these kind of sites use some hidden input when submitting. – pmg Jun 08 '22 at 12:15
  • Maybe your console compiler uses 64-bits integers? Those are very large numbers. – pmg Jun 08 '22 at 12:18
  • How big is `n` in those tests? – Bob__ Jun 08 '22 at 12:19
  • The question is from [leetcode](https://leetcode.com/problems/n-queens-ii/) @pmg. I tried to code same technique in python and it worked fine. So, local run has same answer as OEIS. But it is throwing error in `c`. Interested in finding if error is logical, syntactical or runtime. And what exactly is the error? – Rohit Singh Jun 08 '22 at 12:20
  • Can you write your code without the global variables `N` and `count`? **Global variables are bad** – pmg Jun 08 '22 at 12:23
  • Second that @pmg, **Global Variables should be avoided**. But, I guess I won't be able to write without `count` being global. Moreover, even if they are bad, they should be wrong output on console as well. Then, why on console they are giving correct output but giving wrong on Submission. Can you write answer or provide some good reference for the same *[as rules of site advices to avoid extended conversation on Comments]*? – Rohit Singh Jun 08 '22 at 12:27
  • Edit the question to provide a [mre]. – Eric Postpischil Jun 08 '22 at 12:29
  • @RohitSingh: No, it does not. It is not complete. Other people should be able to compile and execute the code with **no** changes or additions. Since there is no `main` routine and no code that calls `totalNQueens`, other people cannot execute the code. We should not have to guess what else you have in the program to make it run. If the software you are using to submit the code takes just the code shown and supplies its own `main`, you need to show the full specification of the assignment so that other people can reproduce how it executes the software. – Eric Postpischil Jun 08 '22 at 12:35
  • I have attempted to add [**reprex**](https://stackoverflow.com/help/minimal-reproducible-example) code in the body. Do check @EricPostpischil – Rohit Singh Jun 08 '22 at 13:11
  • 1
    Can you see the [problem](https://godbolt.org/z/97TTTjboW) caused by that variable beeing global and not resetted? – Bob__ Jun 08 '22 at 13:15
  • Yes, @Bob__ Got the hint. Thanks a Ton! Can you please provide more reference on why it happened, how to reset the variable, and what changes should I make in the solution? Since, site advices to avoid extended conversation on Comments, I request you to write an answer for the same. Thanks Again! – Rohit Singh Jun 08 '22 at 13:21

2 Answers2

1

The main problem is how the variable count is used.

int N;
int count=0;           // <-- Declaration of count as a GLOBAL variable

void rowExpansion(int r, int cols, int diags, int aDiags);

int totalNQueens(int n)
{
    N=n;
    rowExpansion(0,0,0,0);

    return count;            // <-- Here it's returned
}

void rowExpansion(int r, int cols, int diags, int aDiags)
{
    if (r<N)
    {
        for (register int c=0; c<N; c++)
        {
            // ...

            rowExpansion(r+1, cols | 1<<c, diags | 1<<cD, aDiags | 1<<cAd);
        }
    }
    else count++;  // <-- Here it's modified
}

If the function totalNQueens is called more than once, it just accumulates the count, because count is never resetted.

The OP mention a Python code that "works" as expected, but it's not provided, so we can only guess that it doesn't have this bug.

I'd suggest not to use a global variable, in the first place. The following is a possible alternative implementation.

Note that I also used an unsigned type to perform all the bit manipulations.

#include <stdio.h>
#include <limits.h>

long long int rowExpansion( unsigned N
                          , unsigned r
                          , unsigned cols
                          , unsigned diags
                          , unsigned aDiags )
{   
  long long int count = 0;
  if ( r < N )
  {
    for ( unsigned c = 0; c < N; c++ )
    {
      unsigned cD = r - c + N;
      unsigned cAd = r + c;

      if ( (cols & (1u << c)) ||
           (diags & (1u << cD)) || 
           (aDiags & (1u << cAd)) ) 
        continue;

      count += rowExpansion( N, r+1
                           , cols | 1u << c
                           , diags | 1u << cD
                           , aDiags | 1u << cAd );
    }
  }
  else {
    count++;
  }
  return count;
}

long long int totalNQueens(int n)
{    
  if ( n < 0 ) {
    return 0;
  }
  if ( (unsigned)n > sizeof(unsigned) * CHAR_BIT ) {
    puts("Too big.");
    return 0;
  }
  return rowExpansion(n, 0, 0, 0, 0);
}

int main(void)
{
  // https://oeis.org/A000170
  // 1, 1, 0, 0, 2, 10, 4, 40, 92, 352, 724 
  for (int i = 0; i <= 10; ++i)
  {
    printf("%lld\n", totalNQueens(i));
  }
}
Bob__
  • 12,361
  • 3
  • 28
  • 42
  • 1
    @RohitSingh In your snippet, `count` is declared outside of any block. Having [file-scope](https://en.cppreference.com/w/c/language/scope#File_scope), by default, it also has [external linkage](https://en.cppreference.com/w/c/language/storage_duration#Linkage) and [static storage duration](https://en.cppreference.com/w/c/language/storage_duration#Storage_duration). In your code, all the (recursive) calls of `rowExpansion` access and modify the same global variable. So does `totalNQueens` (and any other function). – Bob__ Jun 08 '22 at 19:15
  • 1
    @RohitSingh If that function is called multiple times, it returns the *current* value, but it should reset (assign 0) `count` *before* calling `rowExpansion` again, restarting the calculation. – Bob__ Jun 08 '22 at 19:17
  • And there is [no way](https://stackoverflow.com/q/3704864) to reset `count` before calling `rowExpansion` again. Right? – Rohit Singh Jun 08 '22 at 19:19
  • 1
    @RohitSingh Of course you [could](https://godbolt.org/z/5jPE5M97a), but I still suggest to [limit](https://godbolt.org/z/bE1rn7vPE) the scope of your variables as [possible](https://stackoverflow.com/questions/176118/when-is-it-ok-to-use-a-global-variable-in-c), instead. Note that the first version of your question didn't include a [mre] and it still contains more than *one* question. – Bob__ Jun 08 '22 at 19:35
0

Rewriting the code with no external variables (N and count) results in the online judging accepting it.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • (no sarcasm or other offense intended, honest impressed question): Do you have your own account there and actually tried successfully to submit? Or are you referring to a confirmation by OP which I do not see? (me not finding it or it being deleted....) And could you confirm that with globals it is rejected? This would be a new way of convincing competition OPs that proposals for needlessly clean code actually avoid/solve problems. – Yunnosch Jun 08 '22 at 13:48
  • 1
    @Yunnosch: I created an account and tested with and without external variables. – Eric Postpischil Jun 08 '22 at 14:13