文章目录

题解 LGP8662 [蓝桥杯 2018 省 AB] 全球变暖

由 hycqwq 发布

很有生活气息的题

思路

首先想到,我们可以在开始时先统计一下总共有多少个岛,然后再统计水面上升之后有多少个岛,然后一减!

但是我们发现,这样只有 $36\text{pts}$。

于是我们找到了一个错误的数据:

10
..........
.##.......
.###......
..##...##.
.###..###.
..#...###.
.###...#..
..#...###.
.......#..
..........

我们的方法居然会得出 $-3$!

我们来看看,这个地图在水面上升之后会变成什么样:

..........
..........
..#.......
..........
..#....#..
.......#..
..#.......
.......#..
..........
..........

$2$ 个岛分裂成了 $5$ 个岛!难怪输出会错!

再回去看题面:

请你计算:依照科学家的预测,照片中有多少岛屿会被完全淹没。

所以说,如果一个大岛分裂成了若干个小岛,那么这些小岛其实是算在同一个岛力的,因为分裂出它们的那个大岛没有被完全淹没(有露出水面的小岛)。

于是我们还需要在开始时给每个岛一个编号,最后统计水面上的残部有多少个不同的编号,而不是最后岛的个数。

代码

STL 大法好!

#include <iostream>
#include <algorithm>
#include <map>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
#define elif else if

int n, s1 = 0, s2 = 0;//s1是开始时岛屿个数,s2是水面上涨后的
char c1[1005][1005], c2[1005][1005];//c1是开始时的形势,c2是水面上涨后的
int id[1005][1005] = {0};//id表示一个坐标本来属于哪个岛
//因为是否淹没某个坐标的判定需要原来的形势,所以水面上涨后需要另开一个数组
bool f[1005][1005] = {{false}};//记录到没到过某个坐标,初始都没到过
int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};//四个方向,上下左右

//floodfill算法,走遍这个岛的每块陆地
//坐标:(x, y),记录到c数组中(这样写其实是懒),这是第iid号岛(iid, island ID)
void srh(int x, int y, char c[1005][1005], int iid)
{
    f[x][y] = true;//标记到过了
    id[x][y] = iid;//标记是iid号岛
    for (int i = 0; i < 4; i++)
        if (1 <= x + dx[i] && x + dx[i] <= n && 
            1 <= y + dy[i] && y + dy[i] <= n && //如果没有越界
            c[x + dx[i]][y + dy[i]] == '#' && //并且还是陆地
            !f[x + dx[i]][y + dy[i]])//最后还没到过
            srh(x + dx[i], y + dy[i], c, iid);//那就搜过去
}

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            cin >> c1[i][j], c2[i][j] = c1[i][j];//复制一份
    //统计开始前有多少个岛
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)//枚举每个坐标
            if (c1[i][j] == '#' && !f[i][j])//如果是陆地并且没有统计到过
                srh(i, j, c1, ++s1);//统计一下
    //水面开始上涨了
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)//枚举每个坐标
            if (c1[i][j] == '#')//如果这一个地方是陆地,才继续
                for (int k = 0; k < 4; k++)//枚举上下左右
                    if (c1[i + dx[k]][j + dy[k]] == '.')//如果碰到了海
                    {
                        c2[i][j] = '.';//被淹没力
                        break;//再见
                    }
    //统计结束后还有多少个岛有残部
    map<int, int> mp;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            if (c2[i][j] == '#' && mp[id[i][j]] == 0)//发现一块残部并且之前没有发现其所属大岛的其他残部
                mp[id[i][j]] = 1;//标记这个岛有残部,注意是=1而不是++
    for (auto i : mp)//C++11及以上遍历STL容器的方法
        s2 += i.second;//统计有多少个岛有残部
    //最后,答案就是开始时的岛屿个数减去水面上涨后还剩的
    cout << s1 - s2 << endl;
    return 0;
}

暂无评论

发表评论