扫雷,开始想到是遍历,但一直想不出什么好的方法,看了学长的是深搜。。
#include <iostream>
#include <string.h>
using namespace std;
#define MAX 25
int n,m,num[MAX][MAX],dis[5][2]={0,0,1,0,-1,0,0,1,0,-1};
char Map[MAX][MAX];
bool ispos;
// 判断出界
bool isout(int x,int y)
{
if( x<0 || y<0 || x>=n || y>=m ) return 1;
return 0;
}
// 判断五个点是否有小于等于0的位置
bool location(int x,int y)
{
int i,xx,yy;
for(i=0;i<5;++i)
{
xx=x+dis[i][0];
yy=y+dis[i][1];
if( !isout(xx,yy) && num[xx][yy]<=0 ) return false;
}
return true;
}
// 如果放雷,五个点数字减1
void change(int x,int y)
{
int i,xx,yy;
for(i=0;i<5;++i)
{
xx=x+dis[i][0];
yy=y+dis[i][1];
if( isout(xx,yy) ) continue;
--num[xx][yy];
}
}
// 如果该地原来放雷,但是应该不放,回溯,五个点数字加1
void c_back(int x,int y)
{
int i,xx,yy;
for(i=0;i<5;++i)
{
xx=x+dis[i][0];
yy=y+dis[i][1];
if( isout(xx,yy) ) continue;
++num[xx][yy];
}
}
// 判断最后一行是否符合条件
bool judge_final(void)
{
int i;
for(i=0;i<m;++i)
if( num[n-1][i]!=0 ) return false;
return true;
}
// 输出结果
void print(void)
{
int i,j;
for(i=0;i<n;++i)
{
for(j=0;j<m;++j)
cout<<Map[i][j];
cout<<endl;
}
}
void dfs(int x,int y)
{
if( ispos ) return;
if( x==n )
{
if( judge_final() )
{ispos=1;print();}
return;
}
if( y==m ) {dfs(x+1,0);return;}
if( x==0 ) // 首先第一行要进行枚举,不冲突即可
{
if( location(x,y) )
{
Map[x][y]='*';
change(x,y);
dfs(x,y+1);
c_back(x,y);
}
Map[x][y]='.';
dfs(x,y+1);
}
else// 其余行,根据上一行相应位置来判断如果做
{
if( num[x-1][y]==0 ) // 上一行为0,此行不能放雷
{
Map[x][y]='.';
dfs(x,y+1);
}
else if( num[x-1][y]==1 ) // 上一行为1,此行必须放雷,判断四周是否有0情况
if( location(x,y) )
{
Map[x][y]='*';
change(x,y);
dfs(x,y+1);
c_back(x,y);
}
}
}
int main()
{
int i,j,test,t_num;
char c;
cin>>test;
for(t_num=1;t_num<=test;++t_num)
{
cin>>n>>m;
for(i=0;i<n;++i)
for(j=0;j<m;++j)
{
cin>>c;
num[i][j]=c-'0';
}
cout<<"Case "<<t_num<<":"<<endl;
ispos=0;
dfs(0,0);
}
return 0;
}