目录
一. 问题描述
给出n*m的棋盘,上面被标记‘X’的地方需要放置皇后来保护,问最少需要几个皇后能保护所有的‘X’型区域。注意:(1)皇后可以放置在任何'.'或者‘X’上 (2)皇后保护自己所在的这一列,这一行,左右斜对角线方向上的所有格子
二. 题解代码
因为深度不确定,放置不确定,直接搜素数据量太大,所以这里采用迭代加深搜索+回溯的方法,限定每次搜索的深度从而限定时间,防止盲目搜索带来的时间浪费,而且这里限制的深度就是放置皇后的个数。
因为这里每放置一个皇后,就能保护自己这一行,这一列,这两斜的对角线方向。所以我们这里以标记方向状态为标记数组。打破了传统的标记坐标状态从而大大减少了时间。使用vis[4][maxn]来标记方向行,方向列,方向左斜,方向右斜。
(1)vis[0][i]标记i行被保护。
(2)vis[1][j]标记j列被保护。
(3)vis[2][i+j]标记(i,j)左斜对角线方向被标记(这一对角线上x+y全为i+j)。
(4)vis[3][maxn+i-j]标记右斜对角线方向被标记(这一对角线上x-y全为i-j但会出现负数,所以加上maxn变为正数)。
在搜索到达maxed层后,‘X’全部被保护的条件是,每个'X'至少有一个方向vis被标记也就是被保护。其实现代码如下:
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<bits/stdc++.h>
using namespace std;
const int maxn = 11;
int n,m,maxed;
int maps[10][10];
bool vis[4][2*maxn];
bool judge(){//判断‘X'四个方向是否都被保护
for(int i = 0;i<n;i++){
for(int j = 0;j<m;j++){
if(maps[i][j]&&!vis[0][i]&&!vis[1][j]&&!vis[2][i+j]&&!vis[3][maxn+i-j])return false;
}
}
return true;
}
bool dfs(int i,int dep)
{
if(dep==maxed){
if(judge())return true;//如果每个都被保护则结束
return false;
}
for(;i<n;i++){//因为每一行最多放一个皇后就够了,下一个放的皇后一定在上一个皇后的后面行
for(int j = 0;j<m;j++){
bool t1 = vis[0][i],t2 = vis[1][j],t3 = vis[2][i+j],t4 = vis[3][maxn + i - j];//记录原来状态
vis[0][i] = vis[1][j] = vis[2][i+j] = vis[3][maxn + i - j] = true;//保护四个方向
if(dfs(i+1,dep+1))return true;
vis[0][i] = t1,vis[1][j] = t2,vis[2][i+j] = t3,vis[3][maxn + i - j] = t4;//回溯
}
}
return false;
}
int solve()
{
for(maxed = 0;;maxed++){
memset(vis,0,sizeof(vis));//vis记录方向
if(dfs(0,0))return maxed;
}
return -1;
}
int main()
{
int t = 0;
while(scanf("%d",&n)!=EOF&&n){
scanf("%d",&m);
memset(maps,0,sizeof(maps));
t++;
for(int i = 0;i<n;i++){
getchar();//防回车
for(int j = 0;j<m;j++){
char ch;
scanf("%c",&ch);
if(ch=='X')maps[i][j] = 1;
}
}
int ans = solve();
printf("Case %d: %d\n",t,ans);
}
return 0;
}