題目閱讀
1.「擺放棋子」->與走迷宮不同的是棋子擺放出的路徑並不是連續的,也就是說該步的位置和下一步可能包含上下左右相鄰,右上、右下、左上、左下相鄰,甚至會出現隔了幾行、幾列才能放置棋子的情況(根本就不相鄰,這是和迷宮題最突出的不同)。也就是解向量**現部分向量值為空的情況。
2.「任意的兩個棋子不能放在棋盤中的同一行或者同一列」->無疑這是排除了上面情況的上下左右相鄰情況和隔了幾行幾列之後仍在同行或同列的情況(是的,我們還要把隔了幾行幾列之後並不在同行同列的情況考慮為合法情況)。
綜上所述,合法情況包括右上、右下、左上、左下相鄰和隔了幾行幾列之後仍在同行或同列的情況。
舉例來說就是:
1.
2 2#..#
2.
4 3…#…#.…#…
對此,我們能夠想到的對策就是行列標記,也就是建立標記某列或某行已經被訪問過的行列標記陣列來去除「存在兩個棋子放在棋盤中的同一行或者同一列」這一非法情況。類似《計算機組成原理》解碼驅動方式重合法。
3.棋子沒有區別,求解對於給定形狀和大小的棋盤,擺放k個棋子的所有可行的擺放方案c。
毫無疑問,需要尋找沒有重複的棋子擺放方案。就像走迷宮所有的情況搜尋所有可以走的位置,然後嘗試擺放,如果能擺放再去考慮下一步。如果迷宮走完或不可走,再做相應處理。(也就是減治、試探、回溯處理)
是的,我們這次又要搜尋,至於選擇深搜還是廣搜,我們可以通過這篇部落格了解下如何更好的選擇:
簡言之,最短路bfs,所有解dfs。本題考察「所有可行的擺放方案」,所以選擇深搜。接下來就分析下深搜情況。
由於通常深搜方向是從上到下、從左到右,因此可以通過在某層找到可放棋子,再去尋找其它層可放位置。所以可以不考慮行標記,只保留列標記(因為從上到下優先於從左到右,如果你的深搜優先順序相反,那麼標記就與這裡相反)。
input
輸入含有多組測試資料。這裡要注意接受多組資料用while(cin>>n>>k),又因為結束為n或k=-1,那麼就是每組資料的第一行是兩個正整數,n k,用乙個空格隔開,表示了將在乙個n*n的矩陣內描述棋盤,以及擺放棋子的數目。 n <= 8 , k <= n
當為-1 -1時表示輸入結束。
while(cin>>n>>k&&n!=-1&&k!=-1)
又因為要求解「所有可行的擺放方案c」,那麼每次都要初始化擺放方案記錄值、列標記。
sum=0;
memset(a,0,sizeof(a));
(注意memset需要宣告#include
)
隨後的n行描述了棋盤的形狀:每行有n個字元,其中 # 表示棋盤區域, . 表示空白區域(資料保證不出現多餘的空白行或者空白列)。同上,需要接受每組資料時接受不同的圖記錄。又因為圖的結點元素都是字元而且是n*n的矩陣,故記錄圖的儲存結構是二維陣列。注意分配足夠列空間給結束符『\n』。
char map[10][10];
for(i=0;i>map[i];
output
對於每一組資料,給出一行輸出,輸出擺放的方案數目c (資料保證c<2^31)。cout《注意為輸出一行需要endl換行
由於c<231,則可以考慮用int作為記錄方案數的資料型別
int的取值範圍為: -231——231-1
搜尋部分
由題目分析可知需要考慮的遞迴基包括用完棋子、超出範圍、已經訪問過的列、圖中『.』不可放置、某層沒有放置位置。
一般情況
對於訪問位置,我們需要行列變數x,y標記,而我們需要列舉嘗試一層的所有列,可以通過
for(j=0;j實現。而列舉所有行,則可以通過dfs(x+1)表示,也就是說dfs遞迴需要引數x表示行。
對於結點可能存在「已經訪問過的列、圖中『.』不可放置」兩種情況,需要排除這兩種才能安置棋子、繼續向下層搜尋。(減治)
if(!a[j]&&map[x][j]=='#')
安置後,為了防止之後棋子安在同列的情況,需要標記a[j]。又因為dfs需要回溯嘗試所有情況,所以又要在dfs下一層後取消標記。
for(j=0;j退化情況
由於出現隔層可以放置棋子的情況,所以我們x與n、k無關,需要標記放置的棋子數,這裡用可放置棋子數隨著放置逐漸減少來表示棋子數變化。則dfs需要引數表示可放置棋子數
dfs(int x,int step)
由於嘗試該層後沒有找到放置位置,那就需要呼叫下一層搜尋
for(j=0;j是的,我們還要繼續考慮剩下的遞迴基「用完棋子、超出範圍」。
用完棋子無疑對應一種放置情況,所以要做sum++操作。
if(step==0)
而「超出範圍」只可能是行超出範圍,因為列控制由j負責。
if(x>=n)
兩者順序是先判斷是否放置完棋子,再判斷是否超過範圍。因為存在放置完棋子後,x=n的情況。
如:
4 4
...#
..#.
.#..
#...
最後遞迴入口應從第一行(起始座標為(0,0))開始,初試step為k。
dfs(0,k);
綜上,**如下:
#include#includeusing namespace std;
int a[10],n,k,first;
int sum;
char map[10][10];
void dfs(int x,int step)
if(x>=n)
for(j=0;j>n>>k&&n!=-1&&k!=-1)
return 0;
}
POJ 1321 棋盤問題
time limit 1000ms memory limit 10000k total submissions 7007 accepted 3390 description 在乙個給定形狀的棋盤 形狀可能是不規則的 上面擺放棋子,棋子沒有區別。要求擺放時任意的兩個棋子不能放在棋盤中的同一行或者同一列...
poj 1321 棋盤問題
棋盤問題 time limit 1000ms memory limit 10000k total submissions 15365 accepted 7600 description 在乙個給定形狀的棋盤 形狀可能是不規則的 上面擺放棋子,棋子沒有區別。要求擺放時任意的兩個棋子不能放在棋盤中的同一...
POJ 1321 棋盤問題
找到第乙個有 的行開始回溯就可以了 include include using namespace std const int maxn 9 char board maxn maxn bool c maxn int ans,n,k void backtracking int curi,int cnt...