【ybtoj高效进阶 21272】生命游戏(bfs)(二分)

生命游戏

题目链接:ybtoj高效进阶 21272

题目大意

给你一个二维网格,然后给你一个目标图形,包含有生命的点和无生命的点。
然后你初始可以任意选点放置生命,问你最多能通过多少次变换得到这个目标图形。
在一次变换中,一个有生命的点会把它周围四个点中没有生命的点改成有生命的。

思路

我们考虑二分答案。

那接着就是判断答案啦。
首先不难看出它那个扩充就是要哈密顿距离的东西,那我们不妨把目标图形中没有生命的点看做墙,然后跑 bfs 得出每个点距离最近的墙是多少。(也就是从它开始最多能扩充多少次)

那你二分出答案之后我们贪心一下肯定是能放的点都放对吧。
然后暴力走,暴力判断一下是不是所有要的点都有了即可。

代码

#include<cmath> #include<queue> #include<cstdio> #include<cstring> using namespace std; int n, m, ds[1001][1001], cf[1001][1001]; char a[1001][1001]; queue <pair<int, int> > q; int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1}; bool ck(int x, int y) { if (x < 1 || x > n) return 0; if (y < 1 || y > m) return 0; return 1; } void get_dis() {//bfs 找到每个点为起点最多能扩充多少次 memset(ds, 0x7f, sizeof(ds)); for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) if (a[i][j] == '.') { ds[i][j] = 0; q.push(make_pair(i, j)); } while (!q.empty()) { int x = q.front().first, y = q.front().second; q.pop(); for (int i = 0; i < 4; i++) { int xx = x + dx[i], yy = y + dy[i]; if (!ck(xx, yy)) continue; if (ds[xx][yy] == ds[0][0]) { ds[xx][yy] = ds[x][y] + 1; q.push(make_pair(xx, yy)); } } } } bool ck(int x) { while (!q.empty()) q.pop(); memset(cf, 0, sizeof(cf)); for (int i = 1; i <= n; i++)//把不会扩充出去的点都找到弄成有生命的 for (int j = 1; j <= m; j++) if (a[i][j] != '.' && ds[i][j] - 1 >= x) { q.push(make_pair(i, j)); cf[i][j] = x + 1; } else if (a[i][j] == '.') cf[i][j] = -1; while (!q.empty()) {//暴力扩充 int x = q.front().first, y = q.front().second; q.pop(); for (int i = 0; i < 4; i++) { int xx = x + dx[i], yy = y + dy[i]; if (!ck(xx, yy)) continue; if (cf[xx][yy]) continue; cf[xx][yy] = cf[x][y] - 1; if (cf[xx][yy] > 1) q.push(make_pair(xx, yy)); } } for (int i = 1; i <= n; i++)//看一些是否所有点都有生命了 for (int j = 1; j <= m; j++) if (!cf[i][j]) return 0; return 1; } int main() { // freopen("1ysa93e.in", "r", stdin); // freopen("1ysa93e.out", "w", stdout); scanf("%d %d", &n, &m); for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { a[i][j] = getchar(); while (a[i][j] != '.' && a[i][j] != '#') a[i][j] = getchar(); } get_dis(); int l = 1, r = n, ans = 0;//二分答案 while (l <= r) { int mid = (l + r) >> 1; if (ck(mid)) ans = mid, l = mid + 1; else r = mid - 1; } printf("%d", ans); return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/YBTOJ_GXJJ_21272.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(47)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示