棋盘问题
Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 60012 | Accepted: 28756 |
Description
在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案C。
Input
输入含有多组测试数据。
每组数据的第一行是两个正整数,n k,用一个空格隔开,表示了将在一个n*n的矩阵内描述棋盘,以及摆放棋子的数目。 n <= 8 , k <= n
当为-1 -1时表示输入结束。
随后的n行描述了棋盘的形状:每行有n个字符,其中 # 表示棋盘区域, . 表示空白区域(数据保证不出现多余的空白行或者空白列)。
每组数据的第一行是两个正整数,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
本人初学深搜,遇到此题,才发现深搜还是很难的。(PS:以前我遇到的深搜题都是一个模板)。这道题我看了诸位大神的博客后,才真正理解此题。
其实深搜是一种思想,不应该认为记住模板就行了(其他算法也一样)。这道题的关键在于如何去搜,根据题目要求(要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列)。我们可以这样去搜索,我们每次一行一行的遍历棋盘。如果在这一行的一个棋盘位置放上棋子之后。就标记这一列,遍历下面几行的时候就跳过这一列。例如:
...# ..#. .#.. #...
遍历第一行,找到第3列#之后,就把第3列标记。
遍历第二行(由于第三列被标记只遍历第0,1, 2列),找到第2列#之后,把第二列标记。
遍历第三行(由于第三列和第二列被标记了,只遍历第0,1列),找到第1列#之后,把第一列标记。
.......
这道题还有一个关键点,就是不一定下一个棋子的位置就在下一行,可能是下两行、下三行......
这个怎么解决? 其实在每一行遍历完之后,直接遍历下一行(具体内容看了代码就懂了)
#include <iostream>
using namespace std;
char mp[12][12]; //装棋盘
int n, k; //棋盘的大小以及棋子的个数
int cnt, num; //记录次数以及棋子的个数
int vis[12]; //标记棋子所在的那一列
void dfs(int i)
{
if (num == k) {
cnt++;
return ;
}
if (i >= n) {
return ;
}
for (int j = 0; j < n; ++j) {
if (!vis[j] && mp[i][j] == '#') {
num++;
vis[j] = 1;
dfs(i + 1);
vis[j] = 0;
num--;
}
}
dfs(i + 1); //本题的核心, 如果进行到这一步,意味着这一行一个棋子都不放,请认真理解。
}
int main()
{
while (cin >> n >> k) {
if (n == -1 && k == -1)
break;
for (int i = 0; i < n; ++i) { //棋盘的装入
for (int j = 0; j < n; ++j) {
cin >> mp[i][j];
}
}
for (int i = 0; i < 12; ++i) //初始化
vis[12] = 0;
cnt = 0;
num = 0;
dfs(0); //进行深搜
cout << cnt << endl; //输出次数
}
}