棋盘问题(带条件的DFS)
题目
在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放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
思路
这是一道有条件的DFS(深度搜索)问题,题目所要求的数据量并不是很高,所以直接在满足条件的情况下深搜就好!
1.寻找条件
从题目中我们不难找出本题一共有5个限制条件:
1.棋盘的大小:n*n
2.棋子的数量:K个
3.下棋的位置:必须是在#上
4.下棋的位置:行上没有棋子
5.下棋的位置:列上没有棋子
2.应对限制条件
1.棋盘大小
我们在初始化棋盘时,用一个二维数组来代表棋盘,在遍历棋盘时使用两个大小为n的for循环去遍历。
2.棋子的数量
函数DFS()
传入一个变量,表示棋子数量,模拟下棋的棋子变化,每下一次棋,棋子数-1,直到最后棋子数为0时结束这一轮下棋 (做为一种递归退出条件) ,同时也标志着一种可能性的增加
3.下棋的位置
限制条件在#
区域进行操作,将Vis
设置为一个一维数组,表示这一列老子都占了,这就很巧妙的规避了一列有相同棋子的情况,而函数DFS()
传入一个参数,表示所检查的当前行数,每次递归时从当前位置的下一行进行检查,这就很巧妙的规避了一行有相同棋子的问题。
代码
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <string>
#include <queue>
#include <map>
#include <stack>
#include <map>
#include <unordered_map>
#include <vector>
#include <cmath>
#include<string.h>
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define Ios ios::sync_with_stdio(false) , cin.tie(0) , cout.tie(0);
#define mem(a,b) memset(a,b,sizeof(a))
char Map[9][9]; //开一个二维数组代表N*N的棋盘
int vis[50]; //落一个子后,此子所在的一列全满,表示接下来的步骤不可在此列落
int n,k,ans;
void dfs(int m,int k){
//棋子没了直接退出本次模拟,并且方案数++
if(k==0){
ans++;
return ;
}
//两层循环为解决限制条件1
for(int i=m;i<n;i++)
for(int j=0;j<n;j++){
if(Map[i][j]=='.'||vis[j]==1) continue; //有子不落,或者白不落
else{
vis[j]=1; //落一个子后,此子所在的一列全满,表示接下来的步骤不可在此列落
dfs(i+1,k-1); //直接检查下一行,此行不检查保证此行只落一个子,并且棋子数-1
vis[j]=0; //要进行下一轮的模拟了,把上一轮落下的棋子拿掉,这里是回溯的思想
}
}
}
int main(){
while(scanf("%d%d",&n,&k) != EOF){
if(n==-1&&k==-1)
break;
for(int i=0;i<n;i++)
scanf("%s",&Map[i]);
mem(vis,0);
ans=0;
dfs(0,k);
printf("%d\n",ans);
}
return 0;
}