题目大意:给出一个n*m的字符矩阵,'.'代表需要灯光照亮(最多15个),'#'代表不能被照亮,只能在’.‘的地方放灯,一个普通的灯可以照亮其本身+上面+右面,一个特别的灯可以旋转0度,90度,180度,270度,并照亮其相对的本身+上面+右面,仅有一盏特别的灯,其余都是普通灯,求将所有'.'照亮所需要的最少的灯的数量,若不能照亮所有的’.‘,输出-1,若没有'.',输出0
解题思路:因为需要被照亮的地方最多15个,放灯的位置用1表示,不放灯的地方用0表示,表示成一种状态,对于每种状态,枚举某一位置(j)为特殊灯,对状态中其余所有1的位置都看一下是否满足普通灯的条件(上面和右面不能是’#‘),若不能满足,则j位置不能放特殊灯,若能放特殊灯,则检测是否所有’.‘的位置都被照亮了,若都被照亮则将该种状态放灯的数目记录下来,比较求最小。另,在处理过程中,需要用一个数组将点的序号与其坐标相对应
#include <bits/stdc++.h>
using namespace std;
char g[220][220];
pair<int,int> p[20];
int a[220][220];
int d[220][220];
bool f[20];
const int INF = 0x3f3f3f3f;
int main()
{
int n,m;
while(scanf("%d%d",&n,&m) == 2)
{
if(n == 0 && m == 0)break;
for(int i = 1;i <= n;i++)
scanf("%s",g[i]+1);
int cnt = 0;
for(int i = 1;i <= n;i++)
for(int j = 1;j <= m;j++)
if(g[i][j] == '.')
{
p[cnt] = make_pair(i,j);
d[i][j] = cnt++;
}
if(cnt == 0)
{
printf("0\n");
continue;
}
int ans = INF;
int tot = (1<<cnt);
for(int i = 0;i < tot;i++)
{
for(int j = 0;j < cnt;j++)
if(i & (1<<j))///对于i状态的第j个位置放灯
{
for(int k = 0; k < 4;k++)///的4个方向
{
for(int tt = 0;tt < cnt;tt++)///对于cnt个位置,先初始化不放灯
f[tt] = false;
bool flag = true;
for(int t = 0;t < cnt;t++) ///对于第t个位置
{
if(i & (1<<t)) ///若i状态中的每个放灯位置
if(t != j)///若t位不是j位,看上右是否能放灯
{
int x = p[t].first;///t位坐标
int y = p[t].second;
f[d[x][y]] = true;///标记放灯
if(x-1 > 0)
{
if(g[x-1][y] == '#') flag = false;///上边不能放灯
else f[d[x-1][y]] = true; ///上边位置放灯
}
if(y+1 <= m)
{
if(g[x][y+1] == '#')flag = false;///右边不能放灯
else f[d[x][y+1]] = true;///右边放灯
}
if(!flag) break;///若某个放灯位置的右|上不符合放灯,则停止枚举
}
}
if(!flag) continue;///若有某个点不能放灯,则不对其4个方向继续搜索
int x = p[j].first;
int y = p[j].second;
f[d[x][y]] = true; ///否则对j位置进行旋转
if(k == 0) ///0度
{
if(x-1 > 0) ///上方
{
if(g[x-1][y] == '#') flag = false;
else f[d[x-1][y]] = true;
}
if(y+1 <= m) ///右方
{
if(g[x][y+1] == '#')flag = false;
else f[d[x][y+1]] = true;
}
}
else if(k == 1)///90度
{
if(x+1 <= n)///下方
{
if(g[x+1][y] == '#')flag = false;
else f[d[x+1][y]] = true;
}
if(y+1 <= m)///右方
{
if(g[x][y+1] == '#')flag = false;
else f[d[x][y+1]] = true;
}
}
else if(k == 2)///180度
{
if(x+1 <= n)///下方
{
if(g[x+1][y] == '#')flag = false;
else f[d[x+1][y]] = true;
}
if(y-1 > 0)///左方
{
if(g[x][y-1] == '#')flag = false;
else f[d[x][y-1]] = true;
}
}
else///270度
{
if(x-1 > 0)///上方
{
if(g[x-1][y] == '#')flag = false;
else f[d[x-1][y]] = true;
}
if(y-1 > 0)///左方
{
if(g[x][y-1] == '#')flag = false;
else f[d[x][y-1]] = true;
}
}
if(!flag) continue; ///若有某个方向不能放,则放弃j位置旋转
for(int t = 0;t < cnt;t++)///对于cnt个位置,若最后一个位置不能放灯
if(f[t] == false)
{
flag = false;break;
}
if(!flag)continue; ///放弃i状态
int num = 0;
for(int t = 0;t < cnt;t++) ///对于i状态,记录等的个数
if(i & (1<<t))
num++;
ans = min(ans,num);
}
}
}
if(ans == INF)ans = -1;
cout<<ans<<endl;
}
return 0;
}