hdu 3572 Escape 网络流
2015-12-09 10:28
597 查看
题目链接
给一个n*m的图, 里面有一些点, '.'代表空地, '#'代表墙, 不可以走, '@'代表大门, 可以有多个, 'X'代表人, 问所有人都走出大门需要的最短时间, 每一时刻一个格子只能有一个人, 每个时刻只能有一个人从大门走出, 如果不能走出, 输出-1。
先dfs判断是否每个人都能走出, 如果有人不能, 直接输出-1。
从小到大枚举时间, 对于枚举的时间t, 每个格子i, j向它四周以及他本身建边, ( i*m+j+(t-1)*nm, x*m+y+t*nm, 1), 这样就相当于t-1时刻的点向t时刻的点连了一条边。 然后每个出口向汇点连边, (x*m+y+t*nm, 汇点, 1)。 源点向0时刻的每个人连边, 只连一次。
这样每次都跑一遍网络流, 如果结果ans等于人数sum, 那么说明这个时刻是最小时刻, 需要注意的是, 如果结果不等于人数, 那么sum -= ans。
具体看代码。
给一个n*m的图, 里面有一些点, '.'代表空地, '#'代表墙, 不可以走, '@'代表大门, 可以有多个, 'X'代表人, 问所有人都走出大门需要的最短时间, 每一时刻一个格子只能有一个人, 每个时刻只能有一个人从大门走出, 如果不能走出, 输出-1。
先dfs判断是否每个人都能走出, 如果有人不能, 直接输出-1。
从小到大枚举时间, 对于枚举的时间t, 每个格子i, j向它四周以及他本身建边, ( i*m+j+(t-1)*nm, x*m+y+t*nm, 1), 这样就相当于t-1时刻的点向t时刻的点连了一条边。 然后每个出口向汇点连边, (x*m+y+t*nm, 汇点, 1)。 源点向0时刻的每个人连边, 只连一次。
这样每次都跑一遍网络流, 如果结果ans等于人数sum, 那么说明这个时刻是最小时刻, 需要注意的是, 如果结果不等于人数, 那么sum -= ans。
具体看代码。
#include<bits/stdc++.h> using namespace std; #define mem(a) memset(a, 0, sizeof(a)) #define mem1(a) memset(a, -1, sizeof(a)) #define mem2(a) memset(a, 0x3f, sizeof(a)) const int inf = 1061109567; const int dir[][2] = { {-1, 0}, {1, 0}, {0, -1}, {0, 1},{0, 0} }; const int maxn = 2e6+5; int q[maxn*2], head[maxn*2], dis[maxn/10], s, t, m, n, vis[30][30], num, flag, sum; char c[30][30]; struct node { int to, nextt, c; node(){} node(int to, int nextt, int c):to(to), nextt(nextt), c(c){} }e[maxn*2]; void init() { num = flag = sum = 0; mem1(head); } void add(int u, int v, int c) { e[num] = node(v, head[u], c); head[u] = num++; e[num] = node(u, head[v], 0); head[v] = num++; } int bfs() { mem(dis); dis[s] = 1; int st = 0, ed = 0; q[ed++] = s; while(st<ed) { int u = q[st++]; for(int i = head[u]; ~i; i = e[i].nextt) { int v = e[i].to; if(!dis[v]&&e[i].c) { dis[v] = dis[u]+1; if(v == t) return 1; q[ed++] = v; } } } return 0; } int dfs(int u, int limit) { if(u == t) { return limit; } int cost = 0; for(int i = head[u]; ~i; i = e[i].nextt) { int v = e[i].to; if(e[i].c&&dis[v] == dis[u]+1) { int tmp = dfs(v, min(limit-cost, e[i].c)); if(tmp>0) { e[i].c -= tmp; e[i^1].c += tmp; cost += tmp; if(cost == limit) break; } else { dis[v] = -1; } } } return cost; } int dinic() { int ans = 0; while(bfs()) { ans += dfs(s, inf); } return ans; } // 上面是模板 int judge(int x, int y) { if(x>=0&&x<n&&y>=0&&y<m&&c[x][y]!='#'&&!vis[x][y]) return 1; return 0; } int dfs1(int x, int y) { for(int i = 0; i<4; i++) { int tmpx = x+dir[i][0]; int tmpy = y+dir[i][1]; //判断能否走出 if(judge(tmpx, tmpy)) { if(c[tmpx][tmpy]=='@') return 1; vis[tmpx][tmpy] = 1; if(dfs1(tmpx, tmpy)) return 1; } } return 0; } int ok(int deep) { //枚举时间, 每一次枚举, 都在原有的图的基础上继续建边,因为原图已经跑过一遍最大流, 所以每次结束后 int nm = n*m; //总人数都应该减去每一次的结果, 意思是已经有那么多的人跑了出去。 for(int i = 0; i<n; i++) { for(int j = 0; j<m; j++) { if(c[i][j] == '@') { add(i*m+j+nm*deep+2, t, 1); //对于每一时刻, 出口都要向汇点建边。 continue; } if(c[i][j]=='#') continue; for(int k = 0; k<5; k++) { int x = i+dir[k][0]; int y = j+dir[k][1]; if(judge(x, y)) { add(i*m+j+(deep-1)*nm+2, x*m+y+deep*nm+2, 1); //前一时刻的点向这一时刻建边。 } } } } int ans = dinic(); if(ans == sum) return 1; sum -= ans; //这里十分重要啊.... return 0; } int main() { while(~scanf("%d%d", &n, &m)) { init(); for(int i = 0; i<n; i++) scanf("%s", c[i]); s = 0, t = 1; for(int i = 0; i<n; i++) { for(int j = 0; j<m; j++) { if(c[i][j] == 'X') { add(s, i*m+j+2, 1); //源点向0时刻的每一个人连边 mem(vis); sum++; if(!dfs1(i, j)) { flag = 1; } } } } if(flag) { puts("-1"); continue; } mem(vis); int ans; for(ans = 1; ; ans++) { if(ok(ans)) //枚举时间 break; } cout<<ans<<endl; } }
相关文章推荐
- Http1.1特定的状态码
- Java基础---网络编程
- HTTP 1.1的常用请求报头
- 深入安卓大图片处理机制,本地及网络图片不加载到内存预压缩
- http请求响应头信息
- HttpURLConnnection
- 网络营销包含哪些方面的知识听太原郭文军细讲
- 【高可用HA】Apache (3) —— Mac下配置Apache Httpd负载均衡(Load Balancer)之mod_proxy
- Java读取网络图片
- http 使用curl发起https请求
- 校园网的无线网络建设经验谈
- Python Cookie HTTP获取cookie并处理
- HTTP缓存是如何实现
- Java锁--synchronized(转载zi http://zhh9106.iteye.com/blog/2151791)
- [网络管理]全双工与半双工的区别
- 对TCP/IP网络协议的深入浅出归纳
- Struts2的Action中获取HttpServletRequest对象
- 【计算机网络】:填空题复习等详细解答
- 第二十六天-文件系统选型及nfs网络文件进阶
- Installing .NET Core on Ubuntu-摘自网络