Luogu3191 HNOI2007 緊急疏散 二分答案、最大流
阿新 • • 發佈:2018-11-01
題意:自己去看
考慮二分答案。$BFS$預處理出每一個人到每一扇門的最短時間,設二分的值為$mid$,那麼把門拆成$mid$個點,每一個點代表第$1,2,...,mid$秒時的狀態。$i-1$時刻的門向$i$時刻的門連一條流量為$INF$的邊,表示有無限多的人可以在門口等待。每一個門拆出來的$mid$個點向匯點連流量為$1$的邊表示逃出的一個人;源點向每一個人連一條流量為$1$的邊,每一個人對應的點向每一扇門的最早到達時刻對應的點連一條流量為$1$的邊,最後計算最大流是否等於人數即可。
1 #include<bits/stdc++.h>
2 #define MAXN 7010
3 #define XX now.x + dir[i][0]
4 #define YY now.y + dir[i][1]
5 #define INF 0x7fffffff
6 using namespace std;
7
8 struct Edge{
9 int end , upEd , flow;
10 }Ed[MAXN << 6];
11 struct node{
12 int x , y , flo;
13 }now;
14 const int dir[4][2] = {0,1,0,-1,1,0,-1,0};
15 int head[MAXN] , flo[MAXN] , cur[MAXN] , cntEd , cntBlock , N , M , cntDoor , dis[401][161] , mid;
16 map < pair < int , int > , int > door;
17 char c[21][21];
18 bool vis[21][21] , be[MAXN];
19 queue < int > q1;
20 queue < node > q2;
21
22 void bfs(int x , int y){
23 cntBlock++;
24 memset(dis[cntBlock] , 0x3f , sizeof(dis[cntBlock]));
25 q2.push((node){x , y , 0});
26 memset(vis , 0 , sizeof(vis));
27 vis[x][y] = 1;
28 while(!q2.empty()){
29 now = q2.front();
30 q2.pop();
31 for(int i = 0 ; i < 4 ; i++)
32 if(c[XX][YY] != 'X' && !vis[XX][YY]){
33 vis[XX][YY] = 1;
34 if(c[XX][YY] == 'D')
35 dis[cntBlock][door.find(make_pair(XX , YY))->second] = now.flo + 1;
36 else
37 q2.push((node){XX , YY , now.flo + 1});
38 }
39 }
40 }
41
42 bool div(){
43 while(!q1.empty())
44 q1.pop();
45 memset(be , 0 , sizeof(be));
46 q1.push(0);
47 be[0] = flo[0] = 1;
48 while(!q1.empty()){
49 int t = q1.front();
50 q1.pop();
51 for(int i = head[t] ; i ; i = Ed[i].upEd){
52 if(!be[Ed[i].end] && Ed[i].flow){
53 flo[Ed[i].end] = flo[t] + 1;
54 be[Ed[i].end] = 1;
55 if(Ed[i].end == cntBlock + cntDoor * mid + 1){
56 memcpy(cur , head , sizeof(head));
57 return 1;
58 }
59 q1.push(Ed[i].end);
60 }
61 }
62 }
63 return 0;
64 }
65
66 bool dinic(int now){
67 if(now == cntBlock + cntDoor * mid + 1)
68 return 1;
69 for(int &i = cur[now] ; i ; i = Ed[i].upEd)
70 if(flo[Ed[i].end] == flo[now] + 1 && Ed[i].flow)
71 if(dinic(Ed[i].end)){
72 Ed[i].flow--;
73 Ed[i ^ 1].flow++;
74 return 1;
75 }
76 return 0;
77 }
78
79 inline void addEd(int a , int b , int c){
80 Ed[++cntEd].end = b;
81 Ed[cntEd].flow = c;
82 Ed[cntEd].upEd = head[a];
83 head[a] = cntEd;
84 }
85
86 bool check(){
87 memset(head , 0 , sizeof(head));
88 cntEd = 1;
89 int T = cntDoor * mid + cntBlock + 1;
90 for(int i = 0 ; i < cntDoor ; i++)
91 for(int j = 1 ; j < mid ; j++){
92 addEd(mid * i + j + cntBlock , mid * i + j + cntBlock + 1 , INF);
93 addEd(mid * i + j + cntBlock + 1 , mid * i + j + cntBlock , 0);
94 }
95 for(int i = 0 ; i < cntDoor ; i++)
96 for(int j = 1 ; j <= mid ; j++){
97 addEd(mid * i + j + cntBlock , T , 1);
98 addEd(T , mid * i + j + cntBlock , 0);
99 }
100 int cnt = 0;
101 for(int i = 2 ; i < N ; i++)
102 for(int j = 2 ; j < M ; j++)
103 if(c[i][j] == '.'){
104 cnt++;
105 for(int k = 1 ; k <= cntDoor ; k++)
106 if(dis[cnt][k] <= mid){
107 addEd(cnt , mid * (k - 1) + dis[cnt][k] + cntBlock , 1);
108 addEd(mid * (k - 1) + dis[cnt][k] + cntBlock , cnt , 0);
109 }
110 addEd(0 , cnt , 1);
111 addEd(cnt , 0 , 0);
112 }
113 int ans = 0;
114 while(div())
115 while(dinic(0))
116 ans++;
117 return ans == cntBlock;
118 }
119
120 int main(){
121 cin >> N >> M;
122 for(int i = 1 ; i <= N ; i++)
123 for(int j = 1 ; j <= M ; j++){
124 cin >> c[i][j];
125 if(c[i][j] == 'D')
126 door.insert(make_pair(make_pair(i , j) , ++cntDoor));
127 }
128 for(int i = 2 ; i < N ; i++)
129 for(int j = 2 ; j < M ; j++)
130 if(c[i][j] == '.')
131 bfs(i , j);
132 int L = 0 , R = N * M;
133 while(L < R){
134 mid = L + R >> 1;
135 check() ? R = mid : L = mid + 1;
136 }
137 if(R == N * M)
138 cout << "impossible";
139 else
140 cout << R;
141 return 0;
142 }