<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Puzu</title>
    <description>The latest articles on DEV Community by Puzu (@puzu).</description>
    <link>https://dev.to/puzu</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3959385%2F4ff51b64-438c-4c04-b3e7-4c032b03f4d6.jpg</url>
      <title>DEV Community: Puzu</title>
      <link>https://dev.to/puzu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/puzu"/>
    <language>en</language>
    <item>
      <title>N-Queens: What Clicked After I Stopped Brute-Forcing It</title>
      <dc:creator>Puzu</dc:creator>
      <pubDate>Sat, 30 May 2026 13:55:20 +0000</pubDate>
      <link>https://dev.to/puzu/n-queens-what-clicked-after-i-stopped-brute-forcing-it-9cl</link>
      <guid>https://dev.to/puzu/n-queens-what-clicked-after-i-stopped-brute-forcing-it-9cl</guid>
      <description>&lt;p&gt;N-Queens is a classic backtracking problem. The goal is simple: place &lt;code&gt;N&lt;/code&gt; queens on an &lt;code&gt;N×N&lt;/code&gt; chessboard such that no two queens attack each other. In this post, we'll be counting the number of valid arrangements rather than printing the boards themselves.&lt;/p&gt;

&lt;p&gt;When first trying to solve this problem, my intuition, like many others at first, was that iterating over ALL cells to decide the initial position was a good idea. Even worse, I went over ALL positions that each queen invalidates and added them to a list...&lt;/p&gt;

&lt;p&gt;After suffering with that naive O(n · n!) approach, I discovered that the optimal solution uses "three sets": columns (&lt;code&gt;cols&lt;/code&gt;), left diagonal (&lt;code&gt;diag1&lt;/code&gt;), and the right diagonal (&lt;code&gt;diag2&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;And I was mind-blown at how such a simple approach was hidden in plain sight, since a queen can only move in 4 directions: horizontally, vertically, and diagonally (left or right).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4i5xjzb3gywpbdd95j84.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4i5xjzb3gywpbdd95j84.jpg" alt="Queen Directions"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why exactly is this approach better runtime-wise? Because, instead of having to find ALL the positions that EVERY queen on the board invalidates, we store only:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Column index&lt;/li&gt;
&lt;li&gt;Diagonal index (&lt;code&gt;row - col&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Anti-diagonal index (&lt;code&gt;row + col&lt;/code&gt;) (I'll explain where the equations come from later)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Some might wonder why we're using sets instead of lists. List membership checks are O(n) since we'd have to iterate over every element to find a match. However, sets hash each element, so lookup is O(1) on average. Hash collisions can degrade this to O(n) in the worst case, but for integer keys like ours this is practically never an issue.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Diagonals
&lt;/h2&gt;

&lt;p&gt;The two equations &lt;code&gt;row - col&lt;/code&gt; and &lt;code&gt;row + col&lt;/code&gt; looked like math magic to me at first, frankly. However, it's frighteningly simple to understand!&lt;/p&gt;

&lt;p&gt;Let's return to basic algebra! Now I'm sure most of you know what the slope-intercept form of a line is, no?&lt;br&gt;
Well, it's &lt;code&gt;y = mx + b&lt;/code&gt;. In case you don't remember, &lt;code&gt;m&lt;/code&gt; is the slope of the line (how steep it is), &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; are the coordinates of a point on the line, while &lt;code&gt;b&lt;/code&gt; is the y-intercept (where the line crosses the y-axis).&lt;/p&gt;

&lt;p&gt;Now, here's the thing: each diagonal and anti-diagonal has the SAME slope, which are &lt;code&gt;m = 1&lt;/code&gt; and &lt;code&gt;m = -1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So each diagonal/anti-diagonal is basically the same line visually, just with a different starting point.&lt;/p&gt;

&lt;p&gt;Using this information, we can see that &lt;code&gt;b&lt;/code&gt; is what makes each diagonal/anti-diagonal unique, so we find it by rearranging the equations into:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;b1 = y - x   and   b2 = y + x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks familiar? That's where &lt;code&gt;row - col&lt;/code&gt; and &lt;code&gt;row + col&lt;/code&gt; came from!&lt;/p&gt;

&lt;p&gt;Here's a visual of exactly that happening on a real board:&lt;br&gt;
  &lt;iframe src="https://www.youtube.com/embed/gfaMehkuLx8"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  The Code!
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;cols&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;diag1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
&lt;span class="n"&gt;diag2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;   
&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;backtrack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cols&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;diag1&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;diag2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="n"&gt;cols&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;diag1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;diag2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;backtrack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;cols&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;diag1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;diag2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Yes, it's that short)&lt;/p&gt;

&lt;p&gt;We place queens row by row. Before placing a queen at &lt;code&gt;(row, col)&lt;/code&gt;, we check whether that column, diagonal, or anti-diagonal is already occupied. If any of the three sets contain the corresponding index, we skip that column and try the next one.&lt;/p&gt;

&lt;p&gt;The three &lt;code&gt;add&lt;/code&gt; and &lt;code&gt;remove&lt;/code&gt; calls are the backtracking step. We mark the current position as occupied before recursing, then unmark it when we return, so the next iteration starts in a clean state.&lt;br&gt;
We reach &lt;code&gt;row == n&lt;/code&gt; when a queen has been placed in every row from 0 to n-1, which is our base case.&lt;/p&gt;

&lt;p&gt;Remember to write &lt;code&gt;global count&lt;/code&gt;, or else you won't be able to increment &lt;code&gt;count&lt;/code&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;The three sets approach is one of those insights that feels obvious in hindsight. The diagonal equations come from middle-school algebra that most of us already know.&lt;/p&gt;

&lt;p&gt;If you're a beginner in CP (psst, I am too), then N-Queens is one of the best backtracking problems to start with. It's kind of hard, sure, but it helps you get comfortable with backtracking (add-traverse-remove), arguably one of the most fundamental concepts in CP.&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>python</category>
      <category>computerscience</category>
      <category>competitiveprogramming</category>
    </item>
  </channel>
</rss>
