簡單搜尋專題(poj1321 )
poj1321-棋盤問題
Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 70333 Accepted: 33302 Description
在一個給定形狀的棋盤(形狀可能是不規則的)上面擺放棋子,棋子沒有區別。要求擺放時任意的兩個棋子不能放在棋盤中的同一行或者同一列,請程式設計求解對於給定形狀和大小的棋盤,擺放k個棋子的所有可行的擺放方案C。
Input
輸入含有多組測試資料。
每組資料的第一行是兩個正整數,n k,用一個空格隔開,表示了將在一個n*n的矩陣內描述棋盤,以及擺放棋子的數目。 n <= 8 , k <= n
當為-1 -1時表示輸入結束。
隨後的n行描述了棋盤的形狀:每行有n個字元,其中 # 表示棋盤區域, . 表示空白區域(資料保證不出現多餘的空白行或者空白列)。Output
對於每一組資料,給出一行輸出,輸出擺放的方案數目C (資料保證C<2^31)。
Sample Input
2 1 #. .# 4 4 ...# ..#. .#.. #... -1 -1Sample Output
2 1Source
‘#’處是可以放棋子的位置,迴圈中列舉一行中每一列放棋子的方案,可行的方案(達到要求:每行每列都只有一個棋子,剛好把所給的棋子用完)則ans++,具體解釋看程式碼。
這位博主的部落格連結的解釋寫的很明白,我也放上來。
這道題目類似N皇后問題,與之不同的是每一行不一定有棋盤,所以dfs裡要注意不一定是當前行。
思路很簡單,只需從第一行第一個開始搜尋,如果該位置該列沒被標記且為棋盤,那麼在這裡放上棋子,並標記,因為每行每列不能衝突,所以搜尋下一行,比並且棋子數加1。每次搜尋之前先要判斷是否棋子已經用完,如果用完,記錄方案數加1,然後直接返回。直到所有搜尋全部完成,此時已得到全部方案數。
此題還需注意標記陣列僅僅標記某一列上是否有棋子,因為每次遞迴下一行,所以每一行不會有衝突,只需判斷這一列上是否有其他棋子。還要注意修改標記後遞歸回來要及時復原。
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <map>
#include <list>
#include <vector>
#include <stack>
#include <queue>
#include <algorithm>
#include <iostream>
#define mem(a) memset(a,0,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn = 1e3 + 5;
char mp[15][15];
int vis[maxn];
int n,k,ans;
void dfs(int x,int q)//x:行數 c: 所用棋子數
{
if(q == k)//找到了一種方案,停止搜尋
{
ans ++;
return;
}
for(int i = x;i < n;i++)//繼續搜尋下面的行
{
for(int j = 0;j < n;j++)//遍歷各個行的每一列
{
if(mp[i][j] == '#' && !vis[j])//該處是棋盤且之前沒有被出現同列的標記過
{
vis[j] = 1;//標記這一行的該列已經出現過
dfs(i+1,q+1);//搜尋下一行
vis[j] = 0;//下一行的每一列不受影響,把標記全部清空
}
}
}
}
int main()
{
while(scanf("%d%d",&n,&k) != EOF)
{
if(n == -1 && k == -1)
break;
ans = 0;
for(int i = 0;i < n;i++)
{
scanf("%s",mp[i]);
}
dfs(0,0);
cout<<ans<<endl;
}
return 0;
}