1. 程式人生 > >USACO1.5.4 Checker Challenge跳棋的挑戰 解題報告(N皇后 回溯法)

USACO1.5.4 Checker Challenge跳棋的挑戰 解題報告(N皇后 回溯法)

Description

檢查一個如下的6 x 6的跳棋棋盤,有六個棋子被放置在棋盤上,使得每行,每列,每條對角線(包括兩條主對角線的所有對角線)上都至多有一個棋子。 列號

0   1   2   3   4   5   6
  -------------------------
1 |   | O |   |   |   |   |
  -------------------------
2 |   |   |   | O |   |   |
  -------------------------
3 |   |   |   |   |   | O |
  -------------------------
4 | O |   |   |   |   |   |
  -------------------------
5 |   |   | O |   |   |   |
  -------------------------
6 |   |   |   |   | O |   |
  -------------------------

上面的佈局可以用序列2 4 6 1 3 5來描述,第i個數字表示在第i行的相應位置有一個棋子,如下: 行號 1 2 3 4 5 6 列號 2 4 6 1 3 5 這只是跳棋放置的一個解。請遍一個程式找出所有跳棋放置的解。並把它們以上面的序列方法輸出。解按字典順序排列。請輸出前3個解。最後一行是解的總個數。 特別注意: 對於更大的N(棋盤大小N x N)你的程式應當改進得更有效。不要事先計算出所有解然後只輸出,這是作弊。如果你堅持作弊,那麼你登陸USACO Training的帳號將被無警告刪除

Input

一個數字N (6 <= N <= 13) 表示棋盤是N x N大小的。

Output

前三行為前三個解,每個解的兩個數字之間用一個空格隔開。第四行只有一個數字,表示解的總數。

Sample Input

6

Sample Output

2 4 6 1 3 5 
3 6 2 5 1 4 
4 1 5 2 6 3 
4

N皇后,很經典的回溯法題目.不過自己手擼出來還是蠻有成就感的

需要注意的是這個題時間卡的恨死,我開了一個大小最多為13的map居然都T了....

通常我們用a[i][j]來表示第i行j列的狀態,但是n皇后每一行只能有1個皇后,所以一維陣列就能存下所有狀態了

用a[i] = j來表示第i行第j列放置了皇后,就避免了判斷行,然後對於判斷列,我們可以單開一個數組v,記錄列的狀態,比較難處理的是所有對角線的情況

這個題判斷對角線必須採用O(1)的時間複雜度,我們這裡設兩個陣列:第一個判斷這種對角線:↙,規律為行+列為一個固定的數,第二個判斷這種:↘,規律為行-列為固定的數(由於行-列可能為負數,這裡我統一加上12,這樣就不會發生陣列越界的情況了

有了這四種狀態的O(1)的判法,剩下的直接DFS就好

#include <map>
#include <cstdio>
#include <iostream>
#define ifor(a) for(int i=1;i<=a;i++)
 
using namespace std;
const int maxn =  20;
int n;
int a[maxn];
 
int res[maxn];
int cnt = 1;
int sum;
 
bool v[maxn];
 
bool l[maxn*2];
bool r[maxn];
 
void dfs(int k)
{
    if(k == n+1){
        sum ++;
        if(sum <=3){
            ifor(cnt-1){
                if(i>=2)
                    putchar(' ');
                printf("%d",res[i]);
            }
            putchar('\n');
        }
        return;
    }
    ifor(n){
        if(!v[i]&&!r[i+k]&&!l[i-k+12]){        //判斷列和對角線
            a[k] = i;
            r[i+k] = v[i] = l[i-k+12] = true;
            res[cnt++] = i;
            dfs(k+1);
            cnt--;
            r[i+k] = v[i] = l[i-k+12] = false;
        }
    }
}
int main()
{
    scanf("%d",&n);
    dfs(1);
    printf("%d\n",sum);
    return 0;
}