给定一个矩阵,'.'表示空位,'X'表示人,'#'表示墙,'@'表示门,
每分钟每个位置至多只能站一个人,人不能穿越墙,人能从门中出去。
每个人每分钟只能上下左右移动一步,问最少需要多少时间让所有的人出去。
-----------------
//dinic TLE
----------------
(1):把每个点按照天数拆成d个点。
(2):添加源汇点scr和sink。
(3):源点向第0天地图上人所在位置的点连一天容量为1的边。
(4):枚举时间Ti
如果当前枚举的天数为d,那么地图中每个位置第d-1天的点向四周和本身第d天的点(新添加的点)连一条容量为1 的边。
(5):每次枚举只需向残留网络里重新添加一些新的点和边。然后再从源点向汇点跑最大流。
(6):直到前Ti天的最大流总和=总人数时,Ti就是所有的最少天数。
const int inf = 1000000000 ; const int maxn = 10000 , maxm = 500000 ; struct Edge{ int v , f ,next ; Edge(){} Edge(int _v , int _f , int _next):v(_v) ,f(_f),next(_next){} }; int sourse , meet ; int id ; Edge e[maxm*2 + 10] ; int g[maxn + 10] ; void add(int u , int v , int f){ e[++id] = Edge(v , f ,g[u]) ; g[u] = id ; e[++id] = Edge(u , 0 , g[v]) ; g[v] = id ; } int cur[maxn] , pre[maxn] , gap[maxn] , dis[maxn] ; void Init(){ memset(g , -1 , sizeof(g)) ; id = 1 ; } int Sap(int s,int t,int n){ int ans = 0 , aug = inf;//aug表示增广路的流量 int i , v , u = pre[s] = s; for(i = 0 ; i <= n ; i++){ cur[i] = g[i]; dis[i] = gap[i] = 0; } gap[s] = n; bool flag; while(dis[s] < n){ flag = false; for(int &j=cur[u] ; j!=-1 ; j=e[j].next){ //&j v = e[j].v; if(e[j].f > 0 && dis[u] == dis[v] + 1){ flag = true;//找到容许边 aug = min(aug , e[j].f); pre[v] = u; u = v; if(u == t){ ans += aug; while(u != s){ u = pre[u]; e[cur[u]].f -= aug; e[cur[u]^1].f += aug;//注意 } aug = inf; } break;//找到一条就退出 } } if(flag) continue; int mindis = n; for(i = g[u] ; i!=-1 ; i=e[i].next){ v = e[i].v; if(e[i].f > 0 && dis[v] < mindis){ mindis = dis[v]; cur[u] = i; } } if((--gap[dis[u]]) == 0) break; gap[dis[u] = mindis+1]++; u = pre[u]; } return ans; } //------------------------------// int n , m , nn ; char str[30][30] ; bool used[30][30] ; int d[4][2] = {{1,0} ,{-1,0} ,{0,1} ,{0 ,-1}} ; int can(int x , int y){ return 1 <= x && x <= n && 1 <= y && y <= m ; } int isok(int x , int y){ used[x][y] = 1 ; if(str[x][y] == '@') return 1 ; for(int i = 0 ; i < 4 ; i++){ int xx = x + d[i][0] ; int yy = y + d[i][1] ; if(! can(xx , yy)) continue ; if(str[xx][yy] != '#' && !used[xx][yy]){ if(isok(xx , yy)) return 1 ; } } return 0 ; } int summan ; int cannotgoout(){ for(int i = 1 ; i <= n ; i++){ for(int j = 1 ; j <= m ; j++){ if(str[i][j] == 'X'){ summan++ ; memset(used , 0 , sizeof(used)) ; if(! isok(i , j)) return 1 ; } } } return 0 ; } int greed(int t){ int i , j , u , k , x , y , v ; for(i = 1 ; i <= n ; i++){ for(j = 1 ; j <= m ; j++){ if(str[i][j] == '#') continue ; u = (t-1)*n*m + (i-1)*m + j + 2 ; add(u , u+n*m , 1) ; for(k = 0 ; k < 4 ; k++){ x = i + d[k][0] ; y = j + d[k][1] ; if(! can(x , y) ) continue ; if(str[x][y] == '#') continue ; v = t*n*m + (x-1)*m + y + 2 ; add(u , v , 1) ; } } } for(i = 1 ; i <= n ; i++){ for(j = 1 ; j <= m ; j++){ u = t*n*m + (i-1)*m + j + 2 ; if(str[i][j] == '@') add(u , meet , 1) ; } } nn += n*m ; return Sap(sourse , meet , nn) ; } int main(){ int i , j , u , v , sum , t ; while(scanf("%d%d" , &n , &m) != EOF){ for(i = 1 ; i <= n ; i++) scanf("%s" , str[i]+1) ; summan = 0 ; if(cannotgoout()){ puts("-1") ; continue ; } if(summan == 0){ puts("0") ; continue ; } Init() ; sourse = 1 ; meet = 2 ; for(i = 1 ; i <= n ; i++){ for(j = 1 ; j <= m ; j++){ u = (i-1)*m + j + 2 ; if(str[i][j] == 'X') add(sourse , u , 1) ; } } nn = 2 + n*m ; sum = 0 ; t = 0 ; while(sum != summan){ t++ ; sum += greed(t) ; } printf("%d\n" , t) ; } return 0 ; }