拆字游戏+DFS

拆字游戏

题目描述
PiPi喜欢把别人的名字拆开来,比如“螺”就可以拆成“虫田糸”,PiPi的语文学的不是很好,于是她决定使用编程的方式来解决这个问题。
给出一个01矩阵,1占据的部分即为需要拆的字,如果两个1分享一条边,那么它们连通。连通具有传递性,即如果a、b连通,b、c连通,则a、c连通。
连通的一系列1被看做可以拆出的一块,现在PiPi需要输出这些拆出的块(用一个01矩阵表示,并且要求矩阵的大小尽可能的小)。
为了确保输出的顺序尽可能的和书写的顺序一致,PiPi从每个块中选出最左上角的点(最左侧的点中,最靠上的)作为代表点,然后按照代表点从左到右(若相同则按从上到下)的顺序输出所有拆出的块。
输入
多组数据。
输入的第一行为两个正整数N、M,表示01矩阵的大小。
接下来N行,每行M个01字符,描述一个需要拆的字。
对于40%的数据,满足1<=N,M<=10。
对于100%的数据,满足1<=N,M<=500。
额外的样例:
11 17
00000000000000000
00001111111100000
00000000000000000
00111111111111100
00000000100000000
00000010101110000
00000110100011000
00011100100001000
00000010100000000
00000001100000000
00000000000000000
输出
按照代表点从左到右(若相同则按从上到下)的顺序输出所有拆出的块。
对于每个块,先输出其大小,然后用对应的01矩阵表示这个块。
7 13
1111111111111
0000001000000
0000001000000
0000001000000
0000001000000
0000001000000
0000011000000
3 4
0001
0011
1110
1 8
11111111
1 1
1
3 4
1110
0011
0001
样例输入

14 22
0000000000001111111100
0000000000001101101100
0000110000001111111100
0000110000001101101100
0111111110001111111100
0110110110000000000000
0110110110000011000000
0111111110001111111000
0000110000000001100000
0000110110001111111100
0111111111000111111000
0000000010001101101100
0000000000000001100000
0000000000000011100000

样例输出

10 9
000110000
000110000
111111110
110110110
110110110
111111110
000110000
000110110
111111111
000000010
5 8
11111111
11011011
11111111
11011011
11111111
8 8
00110000
11111110
00011000
11111111
01111110
11011011
00011000
00111000

提示
PIPI:大家还记得联通分量这个概念吧~

#include <iostream>
using namespace std;

/*
思路:
从上到下遍历矩阵,碰到没有被访问过的1就从这个点进去执行一遍dfs,类似于涂色
用四个变量在dfs里记录横坐标纵坐标的最大值最小值
遍历过的点要标记为1,输出的时候如果碰到没被访问过但是在输出区间内的点就直接输出0
*/

int n, m;
char map[505][505]; // 图
int sta[505][505]; // 状态数组
int a, b, c, d; // 最大最小横坐标纵坐标
int temps[4][2] = { {1, 0}, {0, 1}, {-1, 0}, {0, -1} }; // dfs的方向
// 初始化四个变量
void init() {
    a = c = 0x7fffffff;
    b = d = 0;
}

// 很普通的dfs
void dfs(int x, int y) {
    // 开始找1
    
    if (x > n || y > m || x <= 0 || y <= 0) return;
    
    else {
        sta[x][y] = 1;
        a = min(a, x);
        b = max(b, x);
        c = min(c, y);
        d = max(d, y);
        for (int i = 0; i < 4; i++) {
            int e = x + temps[i][0], f = y + temps[i][1];
            if (!sta[e][f] && map[e][f] == '1') dfs(e, f);
        }
    }
}

// 遍历当前块
void show() {
    for (int i = a; i <= b; i++) {
        for (int j = c; j <= d; j++) {
            // 需要注意的点
            if (!sta[i][j]) cout << 0;
            else cout << map[i][j];
        }
        cout << endl;
    }
}

int main() {
    while (cin >> n >> m) {
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                sta[i][j] = 0;
                cin >> map[i][j];
            }
        }

        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                init();
                // 碰到符合条件的点直接dfs就行
                if (!sta[j][i] && map[j][i] == '1') {
                    dfs(j, i);
                    cout << b - a + 1 << " " << d - c + 1 << endl;
                    show();
                }        
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值