hdu 3572 Escape 网络流

题目链接

给一个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。

具体看代码。

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define mem(a) memset(a, 0, sizeof(a))
  4 #define mem1(a) memset(a, -1, sizeof(a))
  5 #define mem2(a) memset(a, 0x3f, sizeof(a))
  6 const int inf = 1061109567;
  7 const int dir[][2] = { {-1, 0}, {1, 0}, {0, -1}, {0, 1},{0, 0} };
  8 const int maxn = 2e6+5;
  9 int q[maxn*2], head[maxn*2], dis[maxn/10], s, t, m, n, vis[30][30], num, flag, sum;
 10 char c[30][30];
 11 struct node
 12 {
 13     int to, nextt, c;
 14     node(){}
 15     node(int to, int nextt, int c):to(to), nextt(nextt), c(c){}
 16 }e[maxn*2];
 17 void init() {
 18     num = flag = sum = 0;
 19     mem1(head);
 20 }
 21 void add(int u, int v, int c) {
 22     e[num] = node(v, head[u], c); head[u] = num++;
 23     e[num] = node(u, head[v], 0); head[v] = num++;
 24 }
 25 int bfs() {
 26     mem(dis);
 27     dis[s] = 1;
 28     int st = 0, ed = 0;
 29     q[ed++] = s;
 30     while(st<ed) {
 31         int u = q[st++];
 32         for(int i = head[u]; ~i; i = e[i].nextt) {
 33             int v = e[i].to;
 34             if(!dis[v]&&e[i].c) {
 35                 dis[v] = dis[u]+1;
 36                 if(v == t)
 37                     return 1;
 38                 q[ed++] = v;
 39             }
 40         }
 41     }
 42     return 0;
 43 }
 44 int dfs(int u, int limit) {
 45     if(u == t) {
 46         return limit;
 47     }
 48     int cost = 0;
 49     for(int i = head[u]; ~i; i = e[i].nextt) {
 50         int v = e[i].to;
 51         if(e[i].c&&dis[v] == dis[u]+1) {
 52             int tmp = dfs(v, min(limit-cost, e[i].c));
 53             if(tmp>0) {
 54                 e[i].c -= tmp;
 55                 e[i^1].c += tmp;
 56                 cost += tmp;
 57                 if(cost == limit)
 58                     break;
 59             } else {
 60                 dis[v] = -1;
 61             }
 62         }
 63     }
 64     return cost;
 65 }
 66 int dinic() {
 67     int ans = 0;
 68     while(bfs()) {
 69         ans += dfs(s, inf);
 70     }
 71     return ans;
 72 }
 73 // 上面是模板
 74 int judge(int x, int y) {
 75     if(x>=0&&x<n&&y>=0&&y<m&&c[x][y]!='#'&&!vis[x][y])
 76         return 1;
 77     return 0;
 78 }
 79 
 80 int dfs1(int x, int y) {
 81     for(int i = 0; i<4; i++) {
 82         int tmpx = x+dir[i][0];
 83         int tmpy = y+dir[i][1];         //判断能否走出
 84         if(judge(tmpx, tmpy)) {
 85             if(c[tmpx][tmpy]=='@')
 86                 return 1;
 87             vis[tmpx][tmpy] = 1;
 88             if(dfs1(tmpx, tmpy))
 89                 return 1;
 90         }
 91     }
 92     return 0;
 93 }
 94 
 95 int ok(int deep) {          //枚举时间, 每一次枚举, 都在原有的图的基础上继续建边,因为原图已经跑过一遍最大流, 所以每次结束后
 96     int nm = n*m;           //总人数都应该减去每一次的结果, 意思是已经有那么多的人跑了出去。
 97     for(int i = 0; i<n; i++) {
 98         for(int j = 0; j<m; j++) {
 99             if(c[i][j] == '@') {
100                 add(i*m+j+nm*deep+2, t, 1);     //对于每一时刻, 出口都要向汇点建边。
101                 continue;
102             }
103             if(c[i][j]=='#')
104                 continue;
105             for(int k = 0; k<5; k++) {
106                 int x = i+dir[k][0];
107                 int y = j+dir[k][1];
108                 if(judge(x, y)) {
109                     add(i*m+j+(deep-1)*nm+2, x*m+y+deep*nm+2, 1);       //前一时刻的点向这一时刻建边。
110                 }
111             }
112         }
113     }
114     int ans = dinic();
115     if(ans == sum)
116         return 1;
117     sum -= ans;         //这里十分重要啊....
118     return 0;
119 }
120 
121 int main()
122 {
123     while(~scanf("%d%d", &n, &m)) {
124         init();
125         for(int i = 0; i<n; i++)
126             scanf("%s", c[i]);
127         s = 0, t = 1;
128         for(int i = 0; i<n; i++) {
129             for(int j = 0; j<m; j++) {
130                 if(c[i][j] == 'X') {
131                     add(s, i*m+j+2, 1);             //源点向0时刻的每一个人连边
132                     mem(vis);
133                     sum++;
134                     if(!dfs1(i, j)) {
135                         flag = 1;
136                     }
137                 }
138             }
139         }
140         if(flag) {
141             puts("-1");
142             continue;
143         }
144         mem(vis);
145         int ans;
146         for(ans = 1; ; ans++) {
147             if(ok(ans))                 //枚举时间
148                 break;
149         }
150         cout<<ans<<endl;
151     }
152 }

 

posted on 2015-12-09 10:28  yohaha  阅读(271)  评论(0编辑  收藏  举报

导航