Solution -「HNOI」EVACUATE
Sol.
可以发现人的移动除了不能穿墙以外没有别的限制。也就是说人的移动多半不是解题的突破口。
接下来会发现出口的限制很强,即出口每个时刻只能允许一个人出去。
每个时刻?
不难想到对于每一个时刻每一个门,我们单独考虑。也就是说每一个门具有三个属性,横坐标、纵坐标、时间坐标。
于是我们就有了很多很多的门,就可以将限制转换为每一个门都只能让该时刻到达的那一个人出去。
每一扇门仅允许一个人?
这就是一个匹配问题嘛。并且如果考虑人作为左部,门作为右部,这就是一个简单的二分图最大匹配。
显然可以通过 bfs 求得每一个人到任意一扇门所需的时间,设坐标 \((U_x, U_y)\) 这个人到坐标 \((V_x, V_y)\) 这扇门需要 \(t\) 的时间。
也就是说当前这个点对于这扇门可以走所有空间坐标 \((V_x, V_y)\),时间坐标晚于 \(t\) 的所有门。
但这只能判断可行性,难以算出具体答案啊?
容易想到直接二分答案。若当前二分到答案 \(T\),我们就可以将门的空间坐标限制到 \([t, T]\) 内,然后每个人相对于可以走的门连边跑匈牙利即可。
可以简单估算一下二分上界。假设有 \(nm - 1\) 个人,\(1\) 扇门,最远的人到达这扇门的距离是 \(n + m\),而不能发现每个时候都会有人到达,所以上界应是 \(n m\)。
另外一种情况,一个人为了绕开所有墙,最多也只会耗费 \(n m\) 的时间,且对于门来说,\(n m\) 的时间一定可以把人全部送出去。
Code.
#include <queue>
#include <cstdio>
using namespace std;
int Abs(int x) { return x < 0 ? -x : x; }
int Max(int x, int y) { return x > y ? x : y; }
int Min(int x, int y) { return x < y ? x : y; }
int read() {
int k = 1, x = 0;
char s = getchar();
while (s < '0' || s > '9') {
if (s == '-')
k = -1;
s = getchar();
}
while (s >= '0' && s <= '9') {
x = (x << 3) + (x << 1) + s - '0';
s = getchar();
}
return x * k;
}
void write(int x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
void print(int x, char s) {
write(x);
putchar(s);
}
const int MAXM = 2e6 + 5;
const int MAXN = 2e4 + 5;
struct Bipartite_Graph {
struct edge {
int v, nxt;
edge() {}
edge(int V, int Nxt) {
v = V, nxt = Nxt;
}
} e[MAXM << 1];
int head[MAXN], n, cnt;
void Add_Edge(int u, int v) {
e[cnt] = edge(v, head[u]);
head[u] = cnt++;
}
int Mat[MAXN], Tim[MAXN], p[MAXN], len, tot;
void init(int N) {
n = N;
for(int i = 1; i <= n; i++)
head[i] = -1, Tim[i] = 0, Mat[i] = 0;
cnt = 0, tot = 0, len = 0;
}
bool dfs(int u) {
if (Tim[u] == tot)
return false;
Tim[u] = tot;
for (int i = head[u], v; ~i; i = e[i].nxt) {
v = e[i].v;
if (!Mat[v] || dfs(Mat[v])) {
Mat[v] = u;
return true;
}
}
return false;
}
int calc() {
int ans = 0;
for (int i = 1; i <= len; i++) {
tot++;
ans += dfs(p[i]);
}
return ans;
}
} Graph; // 二分图最大匹配模板。
char s[MAXN][MAXN];
int pos[MAXN][MAXN], dist[MAXN][MAXN], n, m, num, cnt;
struct node {
int x, y;
node() {}
node(int X, int Y) {
x = X, y = Y;
}
};
int dir[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
void bfs(node st) { // 求人到门所需时间。
queue<node> q;
q.push(st);
while(!q.empty()) {
node u = q.front(); q.pop();
for(int i = 0, cx, cy; i < 4; i++) {
cx = u.x + dir[i][0], cy = u.y + dir[i][1];
if(cx < 1 || cx > n || cy < 1 || cy > m)
continue;
if(s[cx][cy] == '.' && !dist[pos[st.x][st.y]][pos[cx][cy]]) {
dist[pos[st.x][st.y]][pos[cx][cy]] = dist[pos[st.x][st.y]][pos[u.x][u.y]] + 1;
q.push(node(cx, cy));
}
}
}
}
bool check(int mid) { // 二分可行性判断。
Graph.init(num + (cnt - num) * mid);
for(int i = 1; i <= num; i++) {
Graph.p[++Graph.len] = i;
for(int j = num + 1; j <= cnt; j++)
if(dist[j][i])
for(int k = dist[j][i]; k <= mid; k++)
Graph.Add_Edge(i, j + (k - 1) * (cnt - num)); // 时间范围内 拆点 建边
}
int res = Graph.calc();
return res >= num;
}
int main() {
n = read(), m = read();
num = 0;
for(int i = 1; i <= n; i++) {
scanf ("%s", s[i] + 1);
for(int j = 1; j <= m; j++)
if(s[i][j] == '.')
pos[i][j] = ++num;
}
cnt = num;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
if(s[i][j] == 'D') {
cnt++;
pos[i][j] = cnt;
bfs(node(i, j));
}
// 这部分处理打得有点奇怪了,转整数标号还是按照自己的喜好来。
int l = 0, r = n * m * 2, mid, res = -1;
while(l <= r) {
mid = (l + r) >> 1;
if(check(mid)) {
r = mid - 1;
res = mid;
}
else
l = mid + 1;
}
if(res == -1)
printf("impossible\n");
else
print(res, '\n');
return 0;
}
Solution -「HNOI」EVACUATE的更多相关文章
- Solution -「构造」专练
记录全思路过程和正解分析.全思路过程很 navie,不过很下饭不是嘛.会持续更新的(应该). 「CF1521E」Nastia and a Beautiful Matrix Thought. 要把所有数 ...
- Solution -「原创」Destiny
题目背景 题目背景与题目描述无关.签到愉快. 「冷」 他半靠在床沿,一缕感伤在透亮的眼眸间荡漾. 冷见惆怅而四散逃去.经历嘈杂喧嚣,感官早已麻木.冷又见空洞而乘隙而入.从里向外,这不是感官的范畴. 他 ...
- Solution -「HNOI 2009」「洛谷 P4727」图的同构计数
\(\mathcal{Description}\) Link. 求含 \(n\) 个点的无标号简单无向图的个数,答案模 \(997\). \(\mathcal{Solution}\) 首先 ...
- Solution -「HNOI 2019」「洛谷 P5293」白兔之舞
\(\mathcal{Description}\) Link. 不想概括题意.jpg \(\mathcal{Solution}\) 定义点集 \(S_c=\{(u,v)|v=c\}\):第 ...
- Solution -「HNOI 2007」「洛谷 P3185」分裂游戏
\(\mathcal{Description}\) Link. 给定 \(n\) 堆石子,数量为 \(\{a_n\}\),双人博弈,每轮操作选定 \(i<j\le k\),使 \(a_i ...
- Solution -「GLR-R2」教材运送
\(\mathcal{Description}\) Link. 给定一棵包含 \(n\) 个点,有点权和边权的树.设当前位置 \(s\)(初始时 \(s=1\)),每次在 \(n\) 个结点内 ...
- Solution -「WF2011」「BZOJ #3963」MachineWorks
\(\mathcal{Description}\) Link. 给定你初始拥有的钱数 \(C\) 以及 \(N\) 台机器的属性,第 \(i\) 台有属性 \((d_i,p_i,r_i,g_i ...
- Solution -「LOCAL」二进制的世界
\(\mathcal{Description}\) OurOJ. 给定序列 \(\{a_n\}\) 和一个二元运算 \(\operatorname{op}\in\{\operatorname{ ...
- Solution -「SHOI2016」「洛谷 P4336」黑暗前的幻想乡
\(\mathcal{Description}\) link. 有一个 \(n\) 个结点的无向图,给定 \(n-1\) 组边集,求从每组边集选出恰一条边最终构成树的方案树.对 \(10^9+ ...
随机推荐
- 伪元素选择器,选择器优先级,CSS修改文字属性,CSS修改字体属性,CSS修改其他属性
伪元素选择器 未使用元素选择器的效果 第一行:伪元素选择器:选择部分内容 第二行:伪元素选择器:选择部分内容 伪元素选择器:选择部分内容 伪元素选择器:选择部分内容 ::selection:选择指定元 ...
- 尾递归与 memorize 优化
尾递归与 memorize 优化 本文写于 2020 年 12 月 10 日 递归 递归是一种非常常见的算法思维,在大家刚开始学编程的时候应该就会接触到. 我们可以这么理解递归: function 讲 ...
- Java 15 新特性:隐藏类
什么是隐藏类 隐藏类,是一种不能被其他类直接使用的类.引入隐藏类的主要目的是给框架来使用,使得框架可以在运行时生成类,并通过反射间接使用它们.可能有点抽象,不要紧,下面我们通过一个例子来直观的认识它! ...
- 从头创建一个新的vue项目------用npm|yarn下载vue-cli|vue-ui创建vue
1.下载node或者是nvm node可以直接去node官网下载,http://nodejs.cn/,默认是长期维护的版本 如果想管理node的版本,可以下载nvm.这个是可选的.但是作为一个前端工程 ...
- ML第4周学习小结
本周收获 总结一下本周学习内容: 1.学习了<深入浅出Pandas>的第五章:Pandas高级操作的两个内容 添加修改数据 高级过滤 我的博客链接: Pandas:添加修改.高级过滤 2. ...
- jq命令用法总结
原创:扣钉日记(微信公众号ID:codelogs),欢迎分享,转载请保留出处. 简介 如果说要给Linux文本三剑客(grep.sed.awk)添加一员的话,我觉得应该是jq命令,因为jq命令是用来处 ...
- Flink中如何实现一个自定义MetricReporter
什么是 Metrics 在 flink 任务运行的过程中,用户通常想知道任务运行的一些基本指标,比如吞吐量.内存和 cpu 使用情况.checkpoint 稳定性等等.而通过 flink metric ...
- java中关于while(true)的理解
java中while(true)的理解: while(true)作为无限循环,经常在不知道循环次数的时候使用,并且需要在循环内使用break才会停止,且在run()方法中基本都会写while(true ...
- Go微服务框架go-kratos实战05:分布式链路追踪 OpenTelemetry 使用
一.分布式链路追踪发展简介 1.1 分布式链路追踪介绍 关于分布式链路追踪的介绍,可以查看我前面的文章 微服务架构学习与思考(09):分布式链路追踪系统-dapper论文学习(https://www. ...
- 解决跨海高并发崩溃难题?so easy
近年来随着互联网强势的发展浪潮,越来越多的企业选择跨境出海,扩展海外市场.而想要在一个陌生市场最快速地吸引到用户,一定不能缺少的就是丰富多样的各类活动.然而活动在带来大流量的同时,也带来了一些问题,比 ...