模拟扫雷DFS算法题

洛谷原题:https://www.luogu.com.cn/problem/P2670
样例都过不了,似乎死循环了?是dfs函数写错了吗


#include
#include
#include
using namespace std;

int n,m,cnt;
char c[101][101];
int d[101][101];

int dfs(int a,int b){
    int ans=0;
    if(c[a][b]=='*')ans++;
    for(int i=-1;i<=1;i++)for(int j=-1;j<=1;j++)dfs(a+i,a+j);
    return ans;
}

int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){
        cin>>c[i][j];
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(c[i][j]=='*')cout<<"*";
            else{cnt=dfs(i,j);
            cout<"\n";
        }
    }
    return 0;
}

参考GPT和自己的思路:

从代码中看出,确实存在死循环的可能。首先,在 dfs 函数中,for 循环的条件是从 -1 到 1,即八个方向都要遍历,包括当前位置。因此,在遇到一个格子时,无论它的内容是什么,都会被继续遍历它周围的格子,而周围的格子中又包括了自己,就会陷入死循环。

其次,在 dfs 函数中,没有对已访问过的格子做标记,导致可能会重复遍历同一个格子。

修正这个问题,可以将 for 循环的条件从 -1 到 1 改为 -1 到 2,遍历周围的 8 个格子时去掉自己,同时对已访问过的格子做标记,避免重复访问。以下是修正后的代码:

#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;

int n,m,cnt;
char c[101][101];
bool vis[101][101]; // 标记格子是否访问过
int d[101][101];

int dfs(int a,int b){
    int ans=0;
    if(c[a][b]=='\*') ans++;
    vis[a][b] = true;
    for(int i=-1;i<=1;i++){
        for(int j=-1;j<=1;j++){
            int x = a+i, y = b+j;
            if(i==0 && j==0) continue;
            if(x<1 || x>n || y<1 || y>m) continue;
            if(vis[x][y]) continue;
            ans += dfs(x, y);
        }
    }
    return ans;
}

int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>c[i][j];
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(c[i][j]=='\*') cout<<"\*";
            else{
                cnt=dfs(i,j);
                cout<<cnt;
            }
        }
        cout<<"\n";
    }
    return 0;
}

参考GPT和自己的思路:

问题在于dfs函数中的递归调用时传入的参数不正确。应该传入的是a+i和b+j,而不是a和j。因为a和b是当前格子的坐标,需要加上偏移量才能访问周围的格子。另外,针对边界情况的判断也没有考虑到,需要在dfs函数中加上判断条件,以避免数组越界的情况发生。修正后的dfs函数如下:

int dfs(int a,int b){
    int ans=0;
    if(c[a][b]=='*')ans++;
    for(int i=-1;i<=1;i++){
        for(int j=-1;j<=1;j++){
            int x=a+i,y=b+j;
            if(x>=1&&x<=n&&y>=1&&y<=m&&!(i==0&&j==0)){
                ans+=dfs(x,y);
            }
        }
    }
    return ans;
}

另外,还需要注意输入的矩阵字符使用了反斜杠转义,应该改为普通的星号符号。修改后的完整代码如下:

#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;

int n,m,cnt;
char c[101][101];

int dfs(int a,int b){
    int ans=0;
    if(c[a][b]=='*')ans++;
    for(int i=-1;i<=1;i++){
        for(int j=-1;j<=1;j++){
            int x=a+i,y=b+j;
            if(x>=1&&x<=n&&y>=1&&y<=m&&!(i==0&&j==0)){
                ans+=dfs(x,y);
            }
        }
    }
    return ans;
}

int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>c[i][j];
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(c[i][j]=='*')cout<<"*";
            else{
                cnt=dfs(i,j);
                cout<<cnt;
            }
        }
        cout<<"\n";
    }
    return 0;
}