乍一看蛮恶心的不知道怎么搜索,但是后来一想其实只要枚举第一行的状态,也就是遍历第一行的每个点(按与不按),然后每一行更具上一行来选择按与不按;
即如果map[i-1][j]=='X'那么(i,j)一定要按;
时间复杂度大概就在2^16*n^2;是可以接受的;具体的注释见代码;
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<iostream>
#define INF 1000000
using namespace std;
char map[20][20];//输入;
char cop[20][20];//用于还原map数组的状态;
int n,m;
int ans;
int move[5][2]={{1,0},{-1,0},{0,1},{0,-1},{0,0}};//用于把自己本身以及相邻的四个点反向;
int yes;//用于判断是否可以全部变为白色;
int minans;//输出最小的部数;
int k;//在work函数运行过之后还原ans的值;
void change(int x,int y)//次函数用于改变自己以及相邻的四个点
{
for(int i=0;i<5;i++)
{
int nx=x+move[i][0];
int ny=y+move[i][1];
if(0<=nx&&nx<n&&0<=ny&&ny<m)
{
if(map[nx][ny]=='X')
{
map[nx][ny]='.';
}
else
{
map[nx][ny]='X';
}
}
}
}
void work()
{
k=ans;
for(int i=0;i<n;i++)//记录之前map的状态 方便之后的还原;
{
for(int j=0;j<m;j++)
{
cop[i][j]=map[i][j];
}
}
int ju=0;
for(int i=1;i<n;i++)
{
for(int j=0;j<m;j++)
{
if(map[i-1][j]=='X')
{
change(i,j);
ans++;
}
}
}
for(int j=0;j<m;j++)//判断最后一行是不是都班委白色的了;
{
if(map[n-1][j]=='X')
{
ju=1;
break;
}
}
if(ju==0)
{
yes=1;
}
else
{
yes=0;
}
for(int i=0;i<n;i++)//把map还原到之前的状态;
{
for(int j=0;j<m;j++)
{
map[i][j]=cop[i][j];
}
}
return;
}
void dfs(int y)//用于枚举第一行的状态(即为按与部按)最多也就是2的16次方;
{
if(y==m)//dfs的结束条件;
{
work();
if(yes==1)
{
minans=min(minans,ans);// 如果可以全部变为白色就比较minans与ans的大小;
}
ans=k;//还原ans的值;
return;
}
//下面就是两种情况——按与不按;
change(0,y);
ans++;
dfs(y+1);
ans--;
change(0,y);
dfs(y+1);
return;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
if(n==0&&m==0) break;
for(int i=0;i<n;i++) scanf("%s",map[i]);
ans=0;
yes=0;
minans=INF;
dfs(0);
if(minans==INF)
{
printf("Damaged billboard.\n");
}
else
{
printf("You have to tap %d tiles.\n",minans);
}
}
return 0;
}