题意:一个n*m的矩阵上有一堆敌人X,最少放置几个皇后可全部消灭。
思路:
(1)枚举:枚举矩阵中的每一个位置,用当前index/m 和 index%m 得出 横纵坐标。
(2)标记:同八皇后问题一样,也是标记行,列,对角线。但这个标记不是用于当前是否此点放没放皇后,而是用于检测敌人是否被消灭了。而且,这里没枚举到一个位置时,先将此位置的标记预先继续下来,然后再标记。最后再还原。还原的时候不是直接赋值0,因为有可能不一样,所以要预先记录下来。
(3)判断:遍历整个矩阵中的X位置,看X位置的标记数组是否都标记了,如果都标记了说明皇后都攻击到了,否则没有。
(4)迭代皇后数量,一旦成功,即为最少!
思维一定要开阔,我开始想的是每次看消灭了的敌人数。其实就是利用八皇后的标记,看敌人是否在标记范围内就判断了!!!还有就是标记不是都永远是一个套路 ,本题就是标记数组有变化,需要预先保存后还原!
参考:专攻挖掘机炒鸡蛋算法博客
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 15;
char g[maxn][maxn];
int visit[4][maxn*3];
int n,m,cnt,maxd;
inline bool success(){//寻找每个X障碍物所对应的行,列和对角线是否被标记过
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
if(g[i][j] == 'X' && !visit[0][i] && !visit[1][j] && !visit[2][i+j] && !visit[3][i-j+maxn]) return false;
return true;
}
bool dfs(int index,int d){
if(success()) return true;
if(d == maxd) return false;
for(int pos=index;pos < n*m;pos++){//枚举横纵坐标
int i = pos/m , j = pos%m,save[4];//注意save不能设置全局
save[0] = visit[0][i];save[1] = visit[1][j];save[2] = visit[2][i+j];save[3] = visit[3][i-j+maxn];//将此点之前的标记记录
visit[0][i] = visit[1][j] = visit[2][i+j] = visit[3][i-j+maxn] = 1;//标记此点
if(dfs(pos,d+1)) return true;
visit[0][i] = save[0];visit[1][j] = save[1];visit[2][i+j] = save[2];visit[3][i-j+maxn] = save[3];//还原
}
return false;
}
inline int solve(){//枚举深度即最少皇后
for(maxd=0;maxd<6;maxd++){
memset(visit,0,sizeof(visit));
if(dfs(0,0)) return maxd;
}
return 5;//最大为5
}
int main()
{
int kcase = 1;
while(scanf("%d",&n)!=EOF && n){
scanf("%d",&m);
for(int i=0;i<n;i++) scanf("%s",g[i]);
printf("Case %d: %d\n",kcase++,solve());
}
return 0;
}