1. 程式人生 > >POJ 1321 棋盤問題【DFS/回溯】

POJ 1321 棋盤問題【DFS/回溯】

des 重點 freopen getchar 回溯 return 問題 形狀 tor

棋盤問題
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 62164 Accepted: 29754
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 -1
Sample Output

2
1
Source

蔡錯@pku
【分析】:因為棋子的數量k比棋盤數目n少,那麽可能有些棋盤區不用放置棋子。這是重點,易錯點。
然後這道題和n皇後有點像,要求同一行、同一列不能放棋子,那麽我們一行到下一行逐行搜索,是自然遍歷下來的,不用管;就用一個數組標記列放沒放棋子,沒放過並且是棋盤區就標記可以放,然後回溯(還原現場)進行下一次嘗試。

這裏有兩種情況,由於只要放k個棋子,且有些行不能放棋子。處理當前行的時候有兩種狀態,一是在當前行放棋子,二是不放棋子。放棋子就判斷是否有相同列,不放棋子則直接深搜下一行。
而dfs的辦法也有幾種,常見的有
1.參數只有一個i表示當前搜索行;
2.參數有i表示當前搜索行和cnt表示已填充的棋子數 ;
3.參數有i表示當前搜索行和cnt表示已填充的棋子數 ,雙重循環遍歷,不用考慮放不放
【代碼】:

#include<cstdio>
#include<string>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<cstring>
#include<set>
#include<queue>
#include<algorithm>
#include<vector>
#include<map>
#include<cctype>
#include<stack>
#include<sstream>
#include<list>
#include<assert.h>
#include<bitset>
#include<numeric>
#define debug() puts("++++")
#define gcd(a,b) __gcd(a,b)
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define fi first
#define se second
#define pb push_back
#define sqr(x) ((x)*(x))
#define ms(a,b) memset(a,b,sizeof(a))
#define sz size()
#define be begin()
#define pu push_up
#define pd push_down
#define cl clear()
#define lowbit(x) -x&x
#define all 1,n,1
#define rep(i,n,x) for(int i=(x); i<(n); i++)
#define in freopen("in.in","r",stdin)
#define out freopen("out.out","w",stdout)
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> P;
const int INF = 0x3f3f3f3f;
const LL LNF = 1e18;
const int maxn = 1e6;
const int maxm = 10;
const double PI = acos(-1.0);
const double eps = 1e-8;
const int dx[] = {-1,1,0,0,1,1,-1,-1};
const int dy[] = {0,0,1,-1,1,-1,1,-1};
const int mon[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
const int monn[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int n,k;
char a[maxm][maxm],v[maxm];
int cnt,tot;

void dfs(int i)
{
    if(cnt==k) //當計數器達到k(擺放棋子的數目),不管結果如何都結束了
    {
        tot++;
        return ;
    }
    if(i>=n) return ;
    for(int j=0;j<n;j++)
    {
        if(!v[j] && a[i][j]=='#')
        {
            v[j]=1;
            cnt++;
            dfs(i+1);
            v[j]=0;
            cnt--;
        }
    }
    dfs(i+1); //i行不放棋子
}

int main()
{
    while(~scanf("%d%d",&n,&k))
    {
        tot=cnt=0;
        ms(a,0);
        if(n==-1&&k==-1) break;
        getchar(); //

        for(int i=0;i<n;i++)
            gets(a[i]);

        dfs(0);
        cout<<tot<<endl;
    }
}
/*
2 1
#.
.#
4 4
...#
..#.
.#..
#...
*/

POJ 1321 棋盤問題【DFS/回溯】