hiho刷题日记——第三十一天小Hi小Ho的惊天大作战:扫雷·二

题目

这次的扫雷是二维的了。根据三种规则做一次判断,判断出一定是雷和一定不是雷的数量。
1.如果某一个被探明的格子里所标的数字为0,那么它相邻的8个格子里的未探明格子被认作是一定不是地雷的格子。
2.如果某一个被探明的格子里所标的数字为K,且它相邻的8个格子里正好有K个没有探明的格子的话,则这K个没有探明的格子被认作是一定是地雷的格子。
3.如果某两个探明了的格子A和B,他们中标明的数字分别为P_A和P_B,且他们周围8个格子中没有探明的格子组成的集合分别为S_A和S_B,如果S_A包含S_B,且|S_A|-|S_B|=P_A-P_B,那么S_A-S_B中的所有格子,被认作是一定是地雷的格子。

每次规则的推导应当视作独立的,即不基于任何其他推导出的结论进行。

第1行为2个整数N,M,表示迷宫的大小。
接下来的N行,每行M个整数,为一个矩阵A,用以描述整个迷宫,其中对于每一个格子A[i][j],若A[i][j]=-1,则表示该格子没有被探明,若0<=A[i][j]<=8,则表示该格子已经被探明了,且数值代表该格子附近8个格子中的地雷数。
对于100%的数据,满足:5<=N、M<=2*10^2, -1<=A[i][j]<=8。

思路

。。。。。
首先我规定:矩阵的行数从1开始,到N结束;列数从1开始,到M结束。-2为一定不是雷,-3是一定是雷。
就是根据三种规则写出不同的判断函数。
1.如果某一个被探明的格子里所标的数字为0,那么它相邻的8个格子里的未探明格子被认作是一定不是地雷的格子。

void run1(int x,int y)
{
  for(int i=-1;i<=1;i++) if(x+i>=1 && x+i<=N)
  for(int j=-1;j<=1;j++) if(y+j>=1 && y+j<=M && map[x+i][y+j]==-1)
    map[x+i][y+j]=-2;
}

2.如果某一个被探明的格子里所标的数字为K,且它相邻的8个格子里正好有K个没有探明的格子的话,则这K个没有探明的格子被认作是一定是地雷的格子。

void run2(int x,int y)
{
  if(caculateUnknow(x,y)==map[x][y])
  {
    for(int i=-1;i<=1;i++) if(x+i>=1 && x+i<=N)
    for(int j=-1;j<=1;j++) if(y+j>=1 && y+j<=M && map[x+i][y+j]==-1)
      map[x+i][y+j]=-3;
  }
}

3.如果某两个探明了的格子A和B,他们中标明的数字分别为P_A和P_B,且他们周围8个格子中没有探明的格子组成的集合分别为S_A和S_B,如果S_A包含S_B,且|S_A|-|S_B|=P_A-P_B,那么S_A-S_B中的所有格子,被认作是一定是地雷的格子。

void run3(int x,int y)
{
  for(int i=-2;i<=2;i++) if(x+i>=1 && x+i<=N)
  for(int j=-2;j<=2;j++)
  if(y+j>=1 && y+j<=M && (i || j) && map[x+i][y+j]>=0 && check(x+i,y+j,x,y))
  {
    if(map[x][y]-map[x+i][y+j]==caculateUnknow(x,y)-caculateUnknow(x+i,y+j))
    {
      for(int n=-1;n<=1;n++) if(x+n>=1 && x+n<=N)
      for(int m=-1;m<=1;m++) if(y+m>=1 && y+m<=M && map[x+n][y+m]<0 && !isAdjacent(x+n,y+m,x+i,y+j))
        map[x+n][y+m]=-3;
    }
  }
}

全代码

#include <cstdio>

#define SIZE 201
int N,M,map[SIZE][SIZE];
int count0,count1;

int isAdjacent(int i1, int j1, int i2, int j2)
{
    if ((i1 - i2) > 1 || (i1 - i2)<-1 || (j1 - j2)>1 || (j1 - j2) < -1)
        return 0;
    else
        return 1;
}

int caculateUnknow(int x, int y)
{
    int i, j;
    int count = 0;
  for(int n=-1;n<=1;n++) if(x+n>=1 && x+n<=N)
    for(int m=-1;m<=1;m++) if(y+m>=1 && y+m<=M && map[x+n][y+m]<0)
        count++;
    return count;
}

bool check(int x,int y,int a,int b)
{
    for(int i=-1;i<=1;i++) if(x+i>=1 && x+i<=N)
    for(int j=-1;j<=1;j++) if(y+j>=1 && y+j<=M && map[x+i][y+j]<0 && !isAdjacent(x+i,y+j,a,b))
       return false;
    return true;
}

void run1(int x,int y)
{
  for(int i=-1;i<=1;i++) if(x+i>=1 && x+i<=N)
  for(int j=-1;j<=1;j++) if(y+j>=1 && y+j<=M && map[x+i][y+j]==-1)
    map[x+i][y+j]=-2;
}

void run2(int x,int y)
{
  if(caculateUnknow(x,y)==map[x][y])
  {
    for(int i=-1;i<=1;i++) if(x+i>=1 && x+i<=N)
    for(int j=-1;j<=1;j++) if(y+j>=1 && y+j<=M && map[x+i][y+j]==-1)
      map[x+i][y+j]=-3;
  }
}

void run3(int x,int y)
{
  for(int i=-2;i<=2;i++) if(x+i>=1 && x+i<=N)
  for(int j=-2;j<=2;j++)
  if(y+j>=1 && y+j<=M && (i || j) && map[x+i][y+j]>=0 && check(x+i,y+j,x,y))
  {
    if(map[x][y]-map[x+i][y+j]==caculateUnknow(x,y)-caculateUnknow(x+i,y+j))
    {
      for(int n=-1;n<=1;n++) if(x+n>=1 && x+n<=N)
      for(int m=-1;m<=1;m++) if(y+m>=1 && y+m<=M && map[x+n][y+m]<0 && !isAdjacent(x+n,y+m,x+i,y+j))
        map[x+n][y+m]=-3;
    }
  }
}

int main()
{
  int K;
  scanf("%d",&K);

  while(K--)
  {
    scanf("%d%d",&N,&M);
    for(int i=1;i<=N;i++)
    for(int j=1;j<=M;j++)
    scanf("%d",&map[i][j]);


    for(int i=1;i<=N;i++)
    for(int j=1;j<=M;j++)
    {

      if(!map[i][j])
      {
        run1(i,j);

      }
      if(map[i][j]>0)
      {
        run2(i,j);

        run3(i,j);

      }
    }

    count0=count1=0;
    for(int i=1;i<=N;i++)
    {
        for(int j=1;j<=M;j++)
        {
          if(map[i][j]==-2) count0++;
          if(map[i][j]==-3) count1++;
        }
    }
    printf("%d %d\n",count1,count0);
  }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值