给一个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 }
时间: 2024-12-14 23:41:46