知识点:深搜,剪枝,迭代加深
首先这个题可以使用迭代加深,并且答案会很小,然后是搜索的框架,就是每次寻找一个没有填过的位置去填,然后有两个剪枝,我们用数组来判断某行列主副对角线是否已经放置皇后,这里我们不要在往下递归的循环里面再去加循环了,这个显然是用木桶原理可以解释的,第一个剪枝就是我们如果当前位置所代表的行列主副对角线都有皇后了,那么不需要往下递归,然后第二个剪枝没有想起来,看了别人才知道的,那就是我们当前行放了,那么下次就从下一行开始搜,这个剪枝非常强,接下来主要是思考为什么这个剪枝是对的,
首先这个题对于一种情况,虽然最小的皇后数目是唯一的,但是这几个皇后的摆放方式可能有很多,也就是有很多分支都能搜索到最小的数目,有可能2个皇后放在同一行能消除所有的棋子,但是如果是这样的话,那么一定存在两个皇后不放在同一行,也能消除所有的棋子,这里是常用的思维方式,不把两个棋子放在同一行不会使情况变得更坏,所以我们搜索了一行之后,下一层的搜索等从上一层的行数加一开始搜,这个样子搜索会导致有些能够搜索到正确答案的分支被剪掉,但是至少能留一个正确的分支不被剪掉,只要有一个正确的分支那么我们就能够搜索到答案,所以这样是可行的
总结:搜索问题可能有很多分支能达到答案,剪枝的策略就算能剪掉能得到正确答案的分支也无妨,只要留至少一个能得到正确答案的分支就行了,
#include <bits/stdc++.h>
using namespace std;
const int N = 15;
int n, m, row[N], col[N], dia1[N * 2], dia2[N * 2], h[N][N], flag, max_d, Case;
char s[N][N];
void dfs(int dep, int r) {
if (flag) return;
int ok = 1;
for (int i = 0; i < n && ok; i++) {
for (int j = 0; j < m && ok; j++) {
if (s[i][j] == 'X' && row[i] + col[j] + dia1[i - j + 10] + dia2[i + j] == 0) ok = 0;
}
}
if (ok) {
printf("Case %d: %d\n", ++Case, max_d);
flag = 1;
return;
}
if (dep == max_d) return;
for (int i = r; i < n; i++) {
for (int j = 0; j < m; j++) {
if (h[i][j] || (row[i] && col[i] && dia1[i - j + 10] && dia2[i + j])) continue;
h[i][j] = 1;
row[i]++; col[j]++;
dia1[i - j + 10]++;
dia2[i + j]++;
dfs(dep + 1, i + 1);
h[i][j] = 0;
row[i]--; col[j]--;
dia1[i - j + 10]--;
dia2[i + j]--;
if (flag) return;
}
}
}
int main() {
while (scanf("%d", &n) && n) {
scanf("%d", &m);
for (int i = 0; i < n; i++) {
scanf("%s", s[i]);
}
flag = 0;
for (max_d = 0; ; max_d++) {
dfs(0, 0);
if (flag) break;
}
}
return 0;
}