AcWing 2060. 奶牛选美(BFS + 暴力枚举)题解

文章讲述了如何通过宽度优先搜索(BFS)找出两头斑点牛身上的两个独立斑点,并通过暴力枚举计算最少涂色区域,使得斑点融合,以适应时尚潮流。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述:

听说最近两斑点的奶牛最受欢迎,约翰立即购进了一批两斑点牛。

不幸的是,时尚潮流往往变化很快,当前最受欢迎的牛变成了一斑点牛。

约翰希望通过给每头奶牛涂色,使得它们身上的两个斑点能够合为一个斑点,让它们能够更加时尚。

牛皮可用一个 N×M 的字符矩阵来表示,如下所示:

................
..XXXX....XXX...
...XXXX....XX...
.XXXX......XXX..
........XXXXX...
.........XXX....

其中,X表示斑点部分。

如果两个 X在垂直或水平方向上相邻(对角相邻不算在内),则它们属于同一个斑点,由此看出上图中恰好有两个斑点。

约翰牛群里所有的牛都有两个斑点

约翰希望通过使用油漆给奶牛尽可能少的区域内涂色,将两个斑点合为一个。

在上面的例子中,他只需要给三个 .. 区域内涂色即可(新涂色区域用 ∗∗ 表示):

................
..XXXX....XXX...
...XXXX*...XX...
.XXXX..**..XXX..
........XXXXX...
.........XXX....

请帮助约翰确定,为了使两个斑点合为一个,他需要涂色区域的最少数量。

输入格式

第一行包含两个整数 N 和 M。

接下来 N 行,每行包含一个长度为 M 的由 X和 .构成的字符串,用来表示描述牛皮图案的字符矩阵。

输出格式

输出需要涂色区域的最少数量。

数据范围

1≤N,M≤50

输入样例:
6 16
................
..XXXX....XXX...
...XXXX....XX...
.XXXX......XXX..
........XXXXX...
.........XXX....
输出样例:
3

 解题思路:

根据题目描述,牛上只有两个斑点,可以用BFS宽搜出第一个连通块(即第一个斑点)将其用1标记,再进行宽搜将第二个连通块(即第二个斑点)用2标记。

标记完成后暴力枚举每一个被1标记的点和被2标记的点,枚举所有(1与2)之间的曼哈顿距离(|x1 - x2| + |y1 - y2|)在枚举结果中求出最小值即可。

题解代码(AcWing同步更新):

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>

using namespace std;

const int N = 60;
char matrix[N][N];
int state[N][N];
int dx[4] = {1, -1, 0, 0};//上下左右扩展偏移量 
int dy[4] = {0, 0, 1, -1};
int n, m, flag = 1;

void bfs(int a, int b){
    queue<pair<int, int>> q;
    q.push({a, b});
    state[a][b] = flag;
    while(q.size()){
        pair<int, int> tmp = q.front();
        q.pop();
        for(int i = 0; i < 4; i++){//对上下左右四个方向进行搜索 
            int x = tmp.first + dx[i];
            int y = tmp.second + dy[i];
            if(x < 1 || x > n || y < 1 || y > m){//出界不合法不扩展 
                continue;
            }
            if(state[x][y] != 0){//遍历过不合法 
                continue;
            }
            if(matrix[x][y] != 'X'){//不是斑点不做扩展 
                continue;
            }
            q.push({x, y});//将合法的斑点坐标入队列 
            state[x][y] = flag;//将斑点进行标记(flag初始值为1) 
        }
    }
    flag++;//下一次遍历连通块flag将变成2,用2标记第二个斑点 
}

int main(){
    cin >> n >> m;
    for(int i = 1; i <= n; i++){
        cin >> matrix[i] + 1;
    }
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            if(matrix[i][j] == 'X' && state[i][j] == 0){
                bfs(i, j);
            }
        }
    }
    int res = 999;
    for(int x1 = 1; x1 <= n; x1++){//暴力枚举所有被标记为1的点 
        for(int y1 = 1; y1 <= m; y1++){
            if(state[x1][y1] == 1){
                for(int x2 = 1; x2 <= n; x2++){//暴力枚举所有被标记为2的点 
                    for(int y2 = 1; y2 <= m; y2++){
                        if(state[x2][y2] == 2){
                            int len = abs(x1 - x2) + abs(y1 - y2);//求两点之间的曼哈顿距离 
                            res = min(res, len);//更新最短路径 
                        }
                    }
                }
            }
        }
    }
    cout << res - 1 << endl;
    return 0;
} 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值