CF327D 城市建设

1 CF327D 城市建设

2 题目描述

时间限制 \(2s\) | 空间限制 \(256M\)

\(Iahub\) 开始玩一个叫做“城市建设”的电脑游戏。游戏的目标是在一个 \(n\)\(m\) 列总共 \(n\times m\) 个单元的长方形的格子里修建自己的城市。其中有一些格子中间有孔,不能用来修建任何东西。其他的格子都是空的。在一个格子里 \(Iahub\) 可以修建下列两种类型之一:

  • 蓝颜色的塔,每个塔的人口数量限制在 \(100\) 以内;
  • 红颜色的塔,每个人口数量限制在 \(200\) 以内。红颜色的塔要求旁边至少有一面和蓝颜色的塔相接。

\(Iahub\) 也可以摧毁任何单元内的建筑,而且摧毁次数也不限。摧毁掉一个建筑后那个建筑所在的单元格变空,其他的单元格不受影响。\(Iahub\) 可以劝说别人去他的城市,所以他需要他的城市能容纳尽可能多的人口。他要找出一个修建的顺序使得总的能容纳的人口数越多越好。请找出最佳修建方法。

数据范围:\(1 ≤ n, m ≤ 500\)

3 题解

很显然,对于每一个有 \(n\) 个节点的联通块,我们最多能建造 \(n-1\) 个红颜色的建筑。那么我们考虑如何建造 \(n-1\) 个红颜色的建筑。容易发现,我们最优的构造方案是先将所有可建造区域全部建造上蓝颜色建筑,然后对于每一个联通块,从边缘开始推倒建筑,建红颜色建筑。如何保证一定存在这样的构造方案且正确地输出呢?我们可以找到一个点,将这个点设为那个唯一的不建红颜色建筑的点(根节点)。容易发现,我们可以用 \(dfs\) 遍历所有联通块内的节点。此时,我们递归中找到的叶子结点变成红颜色建筑后,我们可以顺着遍历到它的那一条边找到它的父节点,进而确定所有除了根节点之外的节点都可以变成红颜色建筑,至于建造次序,我们可以按照上述证明中的方案建造。

此时的总操作次数最多是 \((n\times m-1) \times 3 + 1\)(整个图是一个联通图)。由于 \(n, m \le 100\),所以总操作次数最多在 \(3\times 10^4\) 左右,完全符合题目中的 \(10^6\)

4 代码(空格警告):

#include <iostream>
#include <queue>
using namespace std;
const int N = 505;
queue< pair<char, pair<int, int> > > q;
int n, m;
string s;
int dir[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
char Map[N][N];
bool f[N][N];
bool check(int x, int y)
{
    if (x < 1 || y < 1 || x > n || y > m) return 0;
    return 1;
}
void dfs1(int x, int y, int flag)
{
    if (!check(x, y)) return ;
    if (f[x][y]) return ;
    f[x][y] = 1;
    q.push(make_pair('B', make_pair(x, y)));
    for (int i = 0; i < 4; i++)
    {
        int dx = x + dir[i][0];
        int dy = y + dir[i][1];
        dfs1(dx, dy, 0);
    }
    if (!flag)
    {
        q.push(make_pair('D', make_pair(x, y)));
        q.push(make_pair('R', make_pair(x, y)));
    }
}
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        cin >> s;
        for (int j = 1; j <= m; j++)
        {
            Map[i][j] = s[j-1];
            if (Map[i][j] == '#') f[i][j] = 1;
        }
    }
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            if (f[i][j]) continue;
            dfs1(i, j, 1);
        }
    }
    cout << q.size() << '\n';
    while (q.size())
    {
        cout << q.front().first << " " << q.front().second.first << " " << q.front().second.second << '\n';
        q.pop();
    }
    return 0;
}

欢迎关注我的公众号:智子笔记

posted @ 2021-03-30 23:13  David24  阅读(47)  评论(0编辑  收藏  举报