难题·Beihang Couple Pairing Comunity 2017
时间限制: 2000 ms 内存限制: 131072 kb
总通过人数: 10 总提交人数: 15
题目描述
BCPC(Beihang Couple Pairing Community)2017 就要举办了!

这是 Beihang U 一年一度的盛会,在这样的日子里,每一只单身狗们都可以聚集在一起自由地配对, 每一对成功配对的单身狗就可以顺利地进入到最后的礼堂中,接受所有与会单身狗们的祝福,并从脱离狗身,羽化登仙。

ConnorZhong 拉上可爱的三位助教,AlvinZH ,Bamboo,ModricWang 也参与到了这项集会中,可是无一例外,他们都没能成功配对。

最后四个人都被拦在了礼堂外,看着礼堂内哲学的画面,再看看空荡荡的周围,竟然只有这四个人被拦在了外面,这意味着什么?意味着等今晚生米煮成熟饭,这个学校很可能只剩这四只单身狗了啊!

绝望的四个人在寒风中瑟瑟发抖,沙河的风真是喧嚣啊。

”我们不能眼看着这可怕的事情发生!“, AlvinZH,退学大队队长说道。

“那真是天可怕了!”,ConnorZhong应道。

大家一拍即合。ConnorZhong 找来火把,ModricWang 掏出了汽油,AlvinZH 点火,Bamboo 在一边加油…

当夜大礼堂火光冲天 ….

一个礼堂平面图以一个 n×m 的矩阵给出,矩阵中 ‘X’ 为承重墙,不可通行,’.’ 为空地,可以自由通行,E 为紧急疏散传送门出口。在火灾发生的时候,每个 ‘.’ 中都恰好有一对单身狗(2 只!)正在哲学,礼堂中的所有单身狗都尝试从 E 疏散,但是紧急疏散通道的疏散能力有限,每个出口每一秒钟都只能有一对单身狗(2只!)同时疏散。单身狗只能在 . 上通行,且每一秒只能上下左右四个方向移动一个单位,每一个 . 上同一时间可以有多只狗。

注意,地图上只有 ‘.’ 可以任由狗自由通行和堆积,‘E’ 和 ‘X’ 都不可以自由通行和堆积,但是 ‘E’ 每一秒可以由相邻节点的任一对单身狗进入并疏散。

请问最少需要多少秒,礼堂中的单身狗能全部从礼堂中疏散。

如果有单身狗不能从礼堂中撤离,那么请输出一行:“Oh, poor single dog!”.

输入
第一个数为数据组数 T.

每组测试数据第一行为两个正整数 n 和 m ,表示礼堂的大小。

接下来 n 行,每行 m 个字符,表示礼堂的构造。sij(1≤i≤n,1≤j≤m) 为’X’,’.’ 或 E。

T≤20,1≤n≤20,1≤m≤20

输出
对于每组数据,输出一行,如果单身狗们能全部撤离,输出最短撤离时间,否则输出一行"Oh, poor single dog!" (不包含左右引号)。

输入样例
3
3 3
XEX

XEX
3 7
…E
.XXXXXX

3 3
.X.
XEX

输出样例
2
14
Oh, poor single dog!
HINT
有问题欢迎寻找ConnorZhong实锤!


 真的不敢说自己是搞ACM的了,软院的题都狂WA狂T不止,之前有一道dp还看了题解抄了代码…说到底还是自己菜到不行。
 这题刚看的时候立马反应过来是网络流。瞬间想到了之前做过的一道题,是白书上的LA2957 运送超级计算机。然后就依样画葫芦建立分层图。从小到大枚举时间T,然后T增加过程中增加一层图,再在原有基础上增广,一直到流量达到人数为止。其实思路没啥大问题,但是每一次增加400个节点,然后上一层每个‘.’节点要对新一层相邻四个点和本身连边,瞬间点数边数爆炸。结果是TLE,像20*20,右下角一个E的数据,大概二十多秒才出的来。
 后来就开始想能不能忽略时间因素,不建立分层图,只用400个点,然后每一次给 '.‘与‘E’间距离==当前时间 的点对连接新边。小数据发现基本没有问题,但是实际上还是会有两个’.'在同一时刻到达E,出现阻塞的情况,所以必须考虑时间。
 一些典型数据
1
4 4
E . E .
X E. .
X E . .
. E . .
答案 3
1
3 5
. . . X E
. . . . .
. E E . E
答案 4

 后来反思了一下,认为建立分层图的方法中,每个点向下一层连的边是+∞的流量,一般来说这种边很可能是冗余的。上面的方法虽然因为没有分层而错误,但直接为’.'和‘E’建边确实是更好的选择。另外,这时可以发现,‘.’其实是可以不用分层的,只需要为终点’E’分层就行了,这样的话,点数和边数就大大减少了。贴一下代码

#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#include<queue>
#define INF 0x7FFFFFFF
#define maxn 160005
using namespace std;const int dx[5]={0,1,0,-1,0};
const int dy[5]={1,0,-1,0,0};struct edge
{int to,residual;
};char f[25][25];
bool vis[405][25][25];
queue<pair<int,int>> Q[405];
int n,m,k,s,t,nn,mm,flow,T,EN,ans,tag[25][25];
vector<edge> E;
vector<int> G[maxn];
bool flag;
int d[maxn],num[maxn],cur[maxn];int DFS(int x, int a, int fa)
{if(x==t||a==0)return a;int flows=0,f;for(int& i=cur[x];i<G[x].size();i++)   {edge& e=E[G[x][i]];if(d[x]==d[e.to]+1&&(f=DFS(e.to,min(a,e.residual),x))){flows+=f;if(!flag)return flows;e.residual-=f;E[G[x][i]^1].residual+=f;a-=f;if(a==0)return flows;}}int m=n-1;cur[x]=0;for(int i=0;i<G[x].size();i++)if(E[G[x][i]].residual||E[G[x][i]].to==fa&&flows>0)m=min(m,d[E[G[x][i]].to]);if(--num[d[x]]==0)flag=false;num[d[x]=m+1]++;return flows;
}int ISAP_Maxflow()
{for(int i=1;i<=n;i++)d[i]=num[i]=cur[i]=0;num[0]=n;int x=s;flag=true;while(d[s]<n&&flag)flow+=DFS(s,INF,-1);return flow;
}void add_edge(int p, int q, int cap)
{E.push_back((edge){q,cap});E.push_back((edge){p,0});G[p].push_back(E.size()-2);G[q].push_back(E.size()-1);
}void init()
{flow=0;for(int i=1;i<=EN;i++){while(!Q[i].empty())Q[i].pop();}E.clear();for(int i=1;i<=n;i++)G[i].clear();n=2;EN=0;for(int i=1;i<=nn;i++)for(int j=1;j<=mm;j++)if(f[i][j]=='E'){++EN;while(!Q[EN].empty())Q[EN].pop();for(int p=1;p<=nn;p++)for(int q=1;q<=mm;q++)vis[EN][p][q]=false;Q[EN].push({i,j}),++n;   //每次队列扩展出新E要连接的点}for(int i=1;i<=nn;i++)for(int j=1;j<=mm;j++)if(f[i][j]=='.'){add_edge(s,++n,1);  //超级源点到'.'tag[i][j]=n;}
}void expand()
{       for(int i=1,tp;i<=EN;i++){for(int p=1;p<=nn;p++)for(int q=1;q<=mm;q++)vis[i][p][q]=false;add_edge(tp=++n,t,1);queue<pair<int,int>> tmp;while(!Q[i].empty()){pair<int,int> R=Q[i].front();Q[i].pop();for(int k=0,u,v;k<=4;k++){u=R.first+dx[k],v=R.second+dy[k];if(u>0&&u<=nn&&v>0&&v<=mm&&f[u][v]=='.'&&!vis[i][u][v]){tmp.push({u,v}),vis[i][u][v]=true;  //新一层的'E'到超级汇点add_edge(tag[u][v],tp,1);  //'.'到此时新的'E'}}}swap(Q[i],tmp);}
}int main()
{scanf("%d",&T);while(T--){scanf("%d%d",&nn,&mm);int sdog=0;for(int i=1;i<=nn;i++){scanf("%s",f[i]+1);for(int j=1;j<=mm;j++)if(f[i][j]=='.')sdog+=1;}ans=1;flow=0;s=1,t=2;init();expand();for(int lflow=flow;ISAP_Maxflow()<sdog&&flow>lflow;ans++)  //不断增广{expand();lflow=flow;}if(flow>=sdog)printf("%d\n",ans);elseputs("Oh, poor single dog!");}return 0;
}

 因为不可能存在某个时刻没有单身狗到达出口(这样显然不是最优的,每个出口都不接受单身狗,说明此时每队单身狗和他要离开的出口都不相邻,那么肯定有方法让单身狗跟随与之相邻的,上一步离开的单身狗离开,至少会少一对单身狗),因此某个时刻如果流量没有增长,那么一定是被隔离的情况,直接输出无解,不用用bfs再判断图的连通性了。
 其实大家可以发现这个图已经变成一个二分图了,所以没必要用网络流了,直接来一个二分图最大匹配,直接把所有点和边建好,然后按顺序增广,增广到匹配数量达到要求了,看枚举到了哪一天的点,那么答案就是哪一天。
 二分图到底更快速一些…效率无敌。
 其实直接想到用二分图做也不是不可能。可惜思维有点定势了,导致这题思考的时候拐了一个大弯。
 附上mogg的二分图代码,虽然贴别人的代码有点不太道德的样子QwQ

#include <cstdio>
#include <algorithm>
#include <climits>
#include <vector>
#include <queue>
#include <iostream>
#include <string>
#include <map>
#include <unordered_map>
#include <cstdlib>
#include <cstring>
using namespace std;string mp[25];struct Point
{int x, y;
};
bool operator == (const Point&l, const Point&r) { return l.x == r.x && l.y == r.y; }
bool operator < (const Point&l, const Point&r) { return l.x == r.x ? l.y < r.y : l.x < r.x; }
vector< vector<vector<int>>> dis;vector<Point> p, e;
int n, m, k;
const int ms = 666666;
vector<int> G[ms];//邻接矩阵
int match[ms];//记录匹配点
bool visit[ms];//记录是否访问
int ps, os;bool dfs(int x)//寻找增广路径
{int len = G[x].size();for (int i = 0; i < len; ++i){int to = G[x][i];if (!visit[to]){visit[to] = true;if (match[to] == -1 || dfs(match[to])){match[to] = x;return true;}}}return false;
}int MaxMatch()
{int ans = 0;memset(match, -1, sizeof(match));for (int i = 0; i <= n; ++i){memset(visit, false, sizeof(visit));//清空访问if (dfs(i)){ans++;if (ans == ps)return i / os + 1;}}return -1;
}int dir[] = { -1,0,1,0 };
void bfs(const Point& x, int index)
{dis[index][x.x][x.y] = 0;queue<Point> q;q.push(x);int res = 0;while (!q.empty()){Point p1 = q.front();q.pop();for (int i = 0; i < 4; i++){Point p2 = { p1.x + dir[i],p1.y + dir[3 - i] };if (p2.x < n&&p2.x >= 0 && p2.y < m&&p2.y >= 0 && dis[index][p2.x][p2.y] < 0 && mp[p2.x][p2.y] == '.'){dis[index][p2.x][p2.y] = dis[index][p1.x][p1.y] + 1;q.push(p2);}}}
}
int main()
{ios::sync_with_stdio(false);cin.tie(nullptr);int t;cin >> t;while (t--){cin >> n >> m;for (int i = 0; i < n; i++)cin >> mp[i];vector<Point> ot, per;for (int i = 0; i < n; i++)for (int j = 0; j < m; j++)if (mp[i][j] == '.')per.push_back({ i,j });else if (mp[i][j] == 'E')ot.push_back({ i,j });int maxx = n * m;ps = per.size();os = ot.size();dis.clear();dis = vector<vector<vector<int>>>(os, vector<vector<int>>(n, vector<int>(m, -1)));for (int j = 0; j < os; j++)bfs(ot[j], j);for (int i = 0; i < ps; i++)for (int j = 0; j < os; j++){int d = dis[j][per[i].x][per[i].y];if (d >= 0)for (int k = d; k <= maxx; k++)G[(k - 1)*os + j].push_back(maxx*os + i);}n = maxx * os + ps;int res = MaxMatch();if (res == -1)printf("Oh, poor single dog!\n");elseprintf("%d\n", res);for (int i = 0; i < n; i++)G[i].clear();}return 0;
}

 最后总结一下,自己写代码不能总带着思维定势,也不能瞎搞,要冷静分析。
 网络流建图很重要,好的建图效果会大大提升。
 加强练习,不可以有半点松懈!

【网络流】【二分图最大匹配】Buaacoding1043 难题·Beihang Couple Pairing Comunity 2017相关推荐

  1. NYOJ 题目239 月老的难题 (二分图最大匹配-匈牙利算法模板)

    月老的难题 时间限制:1000 ms  |  内存限制:65535 KB 难度:4 描述 月老准备给n个女孩与n个男孩牵红线,成就一对对美好的姻缘. 现在,由于一些原因,部分男孩与女孩可能结成幸福的一 ...

  2. 二分图最大匹配(匈牙利算法,Dinic网络流算法)

    二分图最大匹配 二分图最大匹配问题: 有两个集合A,B,两个集合间有多条边连接集合中的点,且单个集合中的点各不相连,求两集合的点能两两配对的最大匹配数. (参考:)二分图最大匹配--匈牙利算法 匈牙利 ...

  3. 【网络流24题】解题报告:A、飞行员配对方案问题(最大流求二分图最大匹配)

    A.飞行员配对方案问题 (二分图最大匹配)(最大流)[提高+/省选- ] 题目链接 [问题分析] 二分图最大匹配问题. [建模方法] 在二分图的基础上增加源S和汇T. 1.S向X集合中每个顶点连一条容 ...

  4. 51nod 2006 飞行员配对(二分图最大匹配) 裸匈牙利算法 求二分图最大匹配题

    题目: 题目已经说了是最大二分匹配题, 查了一下最大二分匹配题有两种解法, 匈牙利算法和网络流. 看了一下觉得匈牙利算法更好理解, 然后我照着小红书模板打了一遍就过了. 匈牙利算法:先试着把没用过的左 ...

  5. 二分图最大匹配 - 匈牙利算法

    问题描述: X集合(编号1~m),Y集合(编号m+1~n).n,m<100. 给出若干组合(x, y)(相当于映射x->y),问最都能同时有几个组合(分配). 分析: 题目可能简化描述得不 ...

  6. 二分图最大匹配与其应用

    部分定义 传递闭包 一个图.如果图 G G G中点i" role="presentation">iii到点 j j j存在通路,那么在传递闭包中有边i−>j& ...

  7. 二分图最大匹配—匈牙利算法

    二分图:又叫二部图,图G中顶点集V可以分成互不相交的子集(X,Y),并且图中的每一条边所关联的点分别属于两个不同的顶点集,则图G叫二分图.(不含奇环) 二分图的匹配:给定一个二分图G的子图M,M的边集 ...

  8. 51Nod-2006 飞行员配对(二分图最大匹配,匈牙利算法)

    2006 飞行员配对(二分图最大匹配) 题目来源: 网络流24题 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 收藏 关注 第二次世界大战时期,英国皇家空军从沦陷国征募 ...

  9. 二分图最大匹配及最大权匹配

    二分图最大匹配学习 一.二分图的基本知识 二.二分图最大匹配 什么是二分图最大匹配 怎么求二分图最大匹配 三.二分图最大权匹配 四.例题训练 三.最小点覆盖数 一位大佬的神级解释 本以为有了网络流,就 ...

最新文章

  1. Matlab-bp神经网络
  2. 如何用Python从本地将一个文件备份到Google Drive
  3. python统计特定类型文件数量_分享一些常见的Python编程面试题及答案
  4. optee返回REE的几种方式
  5. Butter fly
  6. 常用shader固有函数
  7. 构建时预渲染:网页首帧优化实践
  8. uboot加载linux内核加载那些内容,uBoot和Linux内核中涉及到的几个地址参数的理解...
  9. 软件工程革命 三部曲 —— 前传
  10. weblogic的安装、目录结构、启动
  11. NOIP2002-普及组复赛-第三题-选数
  12. FreeSwitch给会议室人员增加标识
  13. paip.c++ qt 共享库dll的建立
  14. IOS UI TabBar标签栏的使用
  15. Chapter.13 Copy Constructor
  16. Logstash对nginx的access/error.log日志清洗并数据可视化监控设计
  17. 计算机蓝屏了 怎么维修,电脑蓝屏怎么解决
  18. OracleEBSAPP-XLA-95103错误(EventIDcannothaveaNULLvalue.)
  19. 华为云会议,基于云计算的远程会议
  20. java 雷霆战机 教程,java swing实现简单的雷霆战机小游戏项目源码附带视频指导修改教程...

热门文章

  1. 工作N年的开发者更该写博客 —— 记参加CSDN开发者生态汇
  2. ClockGen,旧电脑的超频利器
  3. 童程童美JAVA笔试A卷,童程童美的待遇为什么这么好
  4. js原生alert弹框美化
  5. SEO人员,做好SEO的三大要素有哪些?
  6. 数加平台在数据挖掘项目中的实践
  7. SCSI PRs命令研究总结3 - Linux中的SCSI相关实现
  8. 规则引擎Visual Rules Solution开发基础教程【连载6】--VisualRules实例一
  9. 转型AI ,80后华为Java工程师的故事
  10. 锌掺杂的普鲁士蓝纳米颗粒|微/纳米多孔普鲁士蓝/金复合物|氧化石墨烯/普鲁士蓝/氨基苝四甲酸复合物(GO/PB/PTC-NH2)