二分+二分图匹配+BFS


题意:

墙壁“X”,空区域(都是人)“.”, 门“D”。

人向门移动通过时视为逃脱,门每秒能出去一个人,人可以上下左右移动,墙阻止移动。

求最优移动方案下,最后一个人逃脱的最短时间。如果有人无法安全逃脱(比如被墙围困住),则输出“impossible”。
 
解法1:
以每个门为起点可以通过BFS来算出每个人逃离的最短路。
二分答案,判断所有的人能否在时间T内逃脱。
考虑某一个门,如果能在时间t从该门逃脱的人,应该是距离该门t以内的人,并且其中只有一人能够从该门逃脱。
每个时间和门的二元组,都能确定一个对应的能够从中逃脱的人的集合,而通过计算这个二元组和人组成的二分图的最大匹配数,我们就能判断所有人是否逃脱。
会T。
 const int dx[] = {, , , -};
const int dy[] = {, , -, }; int X, Y;
char field[MAXN][MAXN]; vector<int> dX, dY;
vector<int> pX, pY;
int dist[MAXN][MAXN][MAXN][MAXN]; const int MAXV = ;
int V; //顶点数
vector<int> G[MAXV]; //图的邻接表表示
int match[MAXV]; //所匹配的定点
bool used[MAXV]; // DFS中用到的访问标记 //添加无向边,注意顶点编号从0开始
void add_edge(int u, int v) {
G[u].push_back(v);
G[v].push_back(u);
} //通过DFS寻找增广路
bool dfs(int v) {
used[v] = true;
for (int i = ; i < G[v].size(); i++) {
int u = G[v][i], w = match[u];
if (w < || !used[w] && dfs(w)) {
match[v] = u;
match[u] = v;
return true;
}
}
return false;
} //二分图最大匹配,返回最大匹配数
int Bipartite_Matching() {
int res = ;
memset(match, -, sizeof(match));
for (int v = ; v < V; v++) {
if (match[v] < ) {
memset(used, , sizeof(used));
if (dfs(v)) {
res++;
}
}
}
return res;
} // 二分,这一点非常精妙
bool C(int t) {
// 0~d-1 时间1跑掉的人
// d~2d-1 时间2跑掉的人
// (t-1)d~td-1 时间t跑掉的人
// td~td+p-1 人
int d = dX.size(), p = pY.size();
V = t * d + p;
REP(i, , V) G[i].clear();
REP(i, , d) REP(j, , p) {
int o = dist[dX[i]][dY[i]][pX[j]][pY[j]];
if (o >= ) {
REP(k, o, t + )
add_edge((k - ) * d + i, t * d + j);
}
}
return Bipartite_Matching() == p;
} void bfs(int x, int y, int d[MAXN][MAXN]) {
queue<int> qx, qy;
d[x][y] = ;
qx.push(x);
qy.push(y);
while (!qx.empty()) {
x = qx.front(), qx.pop();
y = qy.front(), qy.pop();
REP(i, , ) {
int nx = x + dx[i];
int ny = y + dy[i];
if (nx >= && nx < X && ny >= && ny < Y &&
field[nx][ny] == '.' && d[nx][ny] < ) {
d[nx][ny] = d[x][y] + ;
qx.push(nx);
qy.push(ny);
}
}
}
} void solve() {
int n = X * Y; //跑最短路
REP(i, , dX.size()) bfs(dX[i], dY[i], dist[dX[i]][dY[i]]); //最短路
int lb = -, ub = n + ;
while (ub - lb > ) {
int mid = (ub + lb) / ;
C(mid) ? ub = mid : lb = mid;
} if (ub > n) {
printf("impossibe\n");
} else {
printf("%d\n", ub);
}
} int main() {
#ifndef ONLINE_JUDGE
// freopen("input.txt", "r", stdin);
#endif // !ONLINE_JUDGE
int T = READ();
while (T--) {
dX.clear(), dY.clear();
pX.clear(), pY.clear();
CLR(field), SET(dist);
X = READ();
Y = READ();
REP(i, , X) cin >> field[i];
REP(i, , X) REP(j, , Y) {
if (field[i][j] == 'D') {
dX.push_back(i);
dY.push_back(j);
} else if (field[i][j] == '.') {
pX.push_back(i);
pY.push_back(j);
}
}
solve();
}
return ;
}

解法2:

在上述解法下,重复的操作是每次二分都会计算一遍匹配,所以T,那么当T增加时,根据增广路的性质,我们只需要不断增加T,每次加1,就可以找出答案。

这个其实现在也不怎么看懂。

下面是不同的地方,一样的没放上来

 void solve() {
int n = X * Y; //跑最短路
REP(i, , dX.size()) bfs(dX[i], dY[i], dist[dX[i]][dY[i]]); // 加边
int d = dX.size(), p = pX.size();
V = X * Y * d + p;
for (int v = ; v < V; ++v) G[v].clear();
REP(i, , d) REP(j, , p) {
int o = dist[dX[i]][dY[i]][pX[j]][pY[j]];
if (o >= ) REP(k, o, n + ) add_edge((k - ) * d + i, n * d + j);
} // 匹配
if (p == ) {
printf("0\n");
return;
}
int num = ; // 匹配数
memset(match, -, sizeof(match));
for (int v = ; v < n * d; v++) {
// n*d是节点总数,每个门一个,把所有情况都跑一遍
memset(used, , sizeof(used));
if (dfs(v)) {
if (++num == p) {
printf("%d\n", v / d + );
return;
}
}
}
printf("impossible\n");
}

(好题)POJ3057的更多相关文章

  1. POJ3057 Evacuation 二分图匹配+最短路

    POJ3057 Evacuation 二分图匹配+最短路 题目描述 Fires can be disastrous, especially when a fire breaks out in a ro ...

  2. java基础集合经典训练题

    第一题:要求产生10个随机的字符串,每一个字符串互相不重复,每一个字符串中组成的字符(a-zA-Z0-9)也不相同,每个字符串长度为10; 分析:*1.看到这个题目,或许你脑海中会想到很多方法,比如判 ...

  3. 【Java每日一题】20170106

    20170105问题解析请点击今日问题下方的"[Java每日一题]20170106"查看(问题解析在公众号首发,公众号ID:weknow619) package Jan2017; ...

  4. 【Java每日一题】20170105

    20170104问题解析请点击今日问题下方的"[Java每日一题]20170105"查看(问题解析在公众号首发,公众号ID:weknow619) package Jan2017; ...

  5. 【Java每日一题】20170104

    20170103问题解析请点击今日问题下方的"[Java每日一题]20170104"查看(问题解析在公众号首发,公众号ID:weknow619) package Jan2017; ...

  6. 【Java每日一题】20170103

    20161230问题解析请点击今日问题下方的"[Java每日一题]20170103"查看(问题解析在公众号首发,公众号ID:weknow619) package Jan2017; ...

  7. SQL面试笔试经典题(Part 1)

    本文是在Cat Qi的原贴的基础之上,经本人逐题分别在MySql数据库中实现的笔记,持续更新... 参考原贴:http://www.cnblogs.com/qixuejia/p/3637735.htm ...

  8. 刷LeetCode的正确姿势——第1、125题

    最近刷LeetCode比较频繁,就购买了官方的参考电子书 (CleanCodeHandbook),里面有题目的解析和范例源代码,可以省去非常多寻找免费经验分享内容和整理这些资料的时间.惊喜的是,里面的 ...

  9. AWS的SysOps认证考试样题解析

    刚考过了AWS的developer认证,顺手做了一下SysOps的样题.以下是题目和答案. When working with Amazon RDS, by default AWS is respon ...

随机推荐

  1. RainbowPlan团队项目-总结

    博客介绍 这个作业属于哪个课程 https://edu.cnblogs.com/campus/xnsy/GeographicInformationScience/ 这个作业要求在哪里 https:// ...

  2. Kdenlive-开始

    版权声明:原创文章,未经博主允许不得转载 这是 Kdenlive 系列文章的第一篇 说明 在 Linux 下的视频编辑的软件并不多,作为其中之一的 kdenlive 在网上的教程就更少了.于是自己琢磨 ...

  3. python,读取txt的方法和应用

    1.读取txt内的百度盘地址,循环保存到百度云中(直接访问下方地址) https://www.cnblogs.com/becks/p/11409467.html 2.读取txt内参数,循环执行查询,读 ...

  4. python数据类型之字典操作

    Python字典是另一种可变容器模型,且可存储任意类型对象,如字符串.数字.元组等其他容器模型.一.创建字典字典由key和value成对组成.基本语法如下: infos = {"name&q ...

  5. python中[-1]、[:-1]、[::-1]、[n::-1]使用方法

    import numpy as np a=np.random.rand(5) print(a) [ 0.64061262 0.8451399 0.965673 0.89256687 0.4851874 ...

  6. Servlet梳理

    Servlet 梳理 概述 Web 技术成为当今主流的互联网 Web 应用技术之一,而 Servlet 是 Java Web 技术的核心基础. 要介绍 Servlet 必须要先把 Servlet 容器 ...

  7. NLP(十九)首次使用BERT的可视化指导

      本文(部分内容)翻译自文章A Visual Guide to Using BERT for the First Time,其作者为Jay Alammar,访问网址为:http://jalammar ...

  8. 手把手实操教程!使用k3s运行轻量级VM

    前 言 k3s作为轻量级的Kubernetes发行版,运行容器是基本功能.VM的管理原本是IaaS平台的基本能力,随着Kubernetes的不断发展,VM也可以纳入其管理体系.结合Container和 ...

  9. 当你的程序在朋友的机器上显示丢失msvcr100d.dll的时候

    0. 给朋友发了个DEMO,收到提示:丢失 msvcr100d.dll 1. 一看是运行库文件,赶紧让朋友下载并安装vc++ 2010 redistribution,朋友反馈还是提示丢失这个dll文件 ...

  10. lua学习之复习汇总篇

    第六日笔记 1. 基础概念 程序块 定义 在 lua 中任何一个源代码文件或在交互模式中输入的一行代码 程序块可以是任意大小的 程序块可以是一连串语句或一条命令 也可由函数定义构成,一般将函数定义写在 ...