1. 程式人生 > 實用技巧 >P1219 [USACO1.5]八皇后 Checker Challenge

P1219 [USACO1.5]八皇后 Checker Challenge

一個如下的6×6的跳棋棋盤,有六個棋子被放置在棋盤上,使得每行、每列有且只有一個,每條對角線(包括兩條主對角線的所有平行線)上至多有一個棋子。

上面的佈局可以用序列246135來描述,第i個數字表示在第i行的相應位置有一個棋子,如下:

行號123456

列號246135

這只是棋子放置的一個解。請編一個程式找出所有棋子放置的解。
並把它們以上面的序列方法輸出,解按字典順序排列。
請輸出前3個解。最後一行是解的總個數。

輸入格式

一行一個正整數n,表示棋盤是n×n大小的。

輸出格式

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

輸入輸出樣例

輸入 #1
6
輸出 #1
2 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
4

說明/提示

【資料範圍】
對於100%的資料,6n13。

題目翻譯來自NOCOW。

USACO Training Section 1.5

瞎雞兒寫(分析)

顯然這道題可以用dfs,而且如果按行搜尋的話,就會預設按字典序輸出答案,省去了排序的過程。

用三個陣列分別標記棋子所在的行,列,對角線是否還能再填入棋子。

對於對角線的處理:按行填入棋子的話,棋子上方的對角線就不需要進行處理了,因為上方已經被表示行的陣列標記過了。

程式碼

```cpp

#include<bits/stdc++.h>
using namespace std;
#define N 100
int r[N][N]//標記行,c[N][N]//標記列,k[N][N]//標記對角線,ans[N]//記錄答案,tot//紀錄合法擺法的個數,n;
void gao(int x,int y,int z)//標記對角線已經被佔用,由於不同棋子的對角線可能會有重合,所以標記用k[][]++,回溯時用k[][]--,
{
if(z==1)
{
for (int i = x, j = y; i <= n || j <= n; i++, j++)
k[i][j] += 1;
for(int i = x, j = y; i <= n || j >= 1; i++, j--)
k[i][j] += 1;
}
else
{
for (int i = x, j = y; i <= n || j <= n; i++, j++)
k[i][j] -= 1;
for(int i = x, j = y; i <= n || j >= 1; i++, j--)
k[i][j] -= 1;
}
}
void dfs(int x)
{
if(x>n)//棋子按行放置完成,輸出答案
{
tot++;
if(tot<=3)
{
for(int i=1;i<=n;i++)
cout << ans[i] <<' ';
cout << endl ;
}
return;
}
for(int i=1;i<=n;i++)//開始判斷
{
if(r[1][i]==0&&c[1][i]==0&&k[x][i]==0)
{
ans[x]=i;
r[1][i]=1,c[1][i]=1;
gao(x,i,1);
dfs(x+1);
r[1][i]=0,c[1][i]=0;
gao(x,i,0);
}
}
}
int main()
{
cin >> n ;
dfs(1);
cout << tot ;
return 0;
}
```