您的位置:首页 > 理论基础 > 计算机网络

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。

具体看代码。

#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;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: