題目:在一個nxm的棋盤上放上最少的皇后,使得給定的目標點(X)全部被占或者被攻擊。
分析:圖論,搜索。利用回溯法求解,需要加入一些剪枝提高效率。
剪枝1:用當前找到的全局最小值剪枝,如果當前使用皇后數量不小於之前的最小值則拋棄;
剪枝2:提前一步判斷,如果當前使用皇后數量+1不小於之前的最小值,且還存在X則拋棄;
剪枝3:每次判斷當前皇后是否能夠消除X,如果不能則無須保留現場和回溯;
說明:跑了5s多╮(╯▽╰)╭。
#include <stdio.h>
#include <stdlib.h>
char maps[9][10];
int max = 0;
void dfs(int n, int m, int d, int v)
{
if (max <= v) {
return;
}
int count = 0;
for (int i = 0; i < n; ++ i) {
for (int j = 0; j < m; ++ j) {
count += (maps[i][j] == 'X');
}
}
if (count == 0) {
max = v;
return;
}else { // 剪枝可以,大幅度提高效率
if (max == v+1) {
return;
}
}
if (d >= n) {
return;
}
char save[9][9], attack = -1;
for (int k = 0; k < m; ++ k) { // 在行內搜索
if (attack) { // 上次沒有有效的 queue,不用再存儲
for (int i = 0; i < n; ++ i) {
for (int j = 0; j < m; ++ j) {
save[i][j] = maps[i][j];
}
}
}
attack = 0; // 統計被攻擊到的目標,并刪除目標
for (int i = 0; i < n; ++ i) {
for (int j = 0; j < m; ++ j) {
if (i == d || j == k || i-d == j-k || i-d == k-j) {
if (maps[i][j] == 'X') {
maps[i][j] = '.';
attack ++;
}
}
}
}
if (attack) { // 存在有效目標則繼續搜索,并回溯
dfs(n, m, d, v+1);
for (int i = 0; i < n; ++ i) {
for (int j = 0; j < m; ++ j) {
maps[i][j] = save[i][j];
}
}
}
}
dfs(n, m, d+1, v); // 搜索下一行
}
int main()
{
int n, m, cases = 1;
while (~scanf("%d",&n) && n) {
scanf("%d",&m);
for (int i = 0; i < n; ++ i) {
scanf("%s",maps[i]);
}
max = (n<m?n:m);
dfs(n, m, 0, 0);
printf("Case %d: %d\n", cases ++, max);
}
return 0;
}
測試數據:
8 8
XXXXXXXX
XXXXXXXX
XXXXXXXX
XXXXXXXX
XXXXXXXX
XXXXXXXX
XXXXXXXX
XXXXXXXX
8 8
X.......
.X......
..X.....
...X....
....X...
.....X..
......X.
.......X
9 2
XX
XX
XX
XX
XX
XX
XX
XX
XX
2 2
XX
XX
5 3
XXX
XXX
XXX
XXX
...
9 9
.X.......
........X
.........
.........
.........
...X.....
.........
..X......
.....X...
9 9
XXXXXXXXX
XXXXXXXXX
XXXX.XXXX
XXX.X.XXX
XX.X.X.XX
XXX.X.XXX
XXXX.XXXX
XXXXXXXXX
XXXXXXXXX
9 9
XXXXXXXXX
XXXXXXXXX
XXXXXXXXX
XXXXXXXXX
XXXXXXXXX
XXXXXXXXX
XXXXXXXXX
XXXXXXXXX
XXXXXXXXX
2 2
..
..
7 7
X......
XX.....
X.X..X.
X......
X..X...
X......
XXXXXXX
0