1. 程式人生 > >POJ Evacuation /// 二分圖最大匹配

POJ Evacuation /// 二分圖最大匹配

題目大意:

在一個n*m的房間中 ‘X’為牆 ‘D’為門 ‘.’為人 

門只存在與外圍 人每秒鐘只能向四連通區域走一步

門比較狹窄 每秒鐘只能通過一個人

求所有人逃脫的最短時間 如果不可能則輸出impossible

 

對每個門 廣搜出能在這個門逃脫的人的逃出時間

將 對應各個時間的這個門 當做不同的點

即 若有d個門 p個人

時間1對應的門編號為 0~d-1

時間2對應的門編號為 d~2*d-1

時間t對應的門編號為 (t-1)*d~t*d-1

然後將人編號為 t*d~t*d+p-1

再將 對應時間的門的編號 與 對應時間在該門逃脫的人 連邊

而一個人若能在 t 時間逃脫 那麼同樣可以在 t+1、t+2、t+3...時間逃脫

所以 對應時間到最晚時間的該門的編號 都可與 這個人連邊

 

這樣找到 各個時間的門 與 人 的最大匹配

時間從小到大 這樣判斷到最大匹配數恰好等於人數時說明此時所有人都可逃脫

#include <bits/stdc++.h>
using namespace std;
int n,m;
char G[15][15];
int mov[4][2]={0,1,1,0,0,-1,-1,0};

int dis[15][15][15][15]; 
// dis[x][y][i][j] 門的位置為xy 人的位置為ij 儲存逃脫的最短用時
struct NODE { int
x,y; }; vector <NODE> D, P; // D記錄門的位置 P記錄人的位置 const int E=12*12*12*15; vector <int> e[E]; // 鄰接表 bool bound(int x1,int y1) { return x1<0 || x1>=n || y1<0 || y1>=m; } void bfs(int x1,int y1,int d[15][15]) { // d為dis[x1][y1]對應的後兩維 queue <NODE> q; q.push((NODE){x1,y1}); d[x1][y1]
=0; while(!q.empty()) { NODE e=q.front(); q.pop(); for(int i=0;i<4;i++) { int x2=e.x+mov[i][0], y2=e.y+mov[i][1]; if(bound(x2,y2) || d[x2][y2]!=-1) continue; if(G[x2][y2]!='.') continue; d[x2][y2]=d[e.x][e.y]+1; q.push((NODE){x2,y2}); } } } /**二分圖最大匹配*/ bool vis[E]; int mat[E]; bool dfs(int u) { vis[u]=1; for(int i=0;i<e[u].size();i++) { int v=e[u][i], d=mat[v]; if(d==-1 || !vis[d]&&dfs(d)) { mat[u]=v, mat[v]=u; return 1; } } return 0; } int match(int d,int p) { int res=0; memset(mat,-1,sizeof(mat)); for(int i=0;i<n*d;i++) // 時間從小到大 一旦找到最大匹配就是最快逃脫時間 if(mat[i]==-1) { memset(vis,0,sizeof(vis)); if(dfs(i)) { res++; if(res==p) return i/d+1; /// 一旦匹配數等於人數 說明此時所有人都已匹配 } } return 0; } /***/ void solve() { memset(dis,-1,sizeof(dis)); D.clear(), P.clear(); for(int i=0;i<n;i++) { for(int j=0;j<m;j++) if(G[i][j]=='D') { D.push_back((NODE){i,j}); bfs(i,j,dis[i][j]);// 廣搜出所有能到ij門的人的最短時間 } else if(G[i][j]=='.') P.push_back((NODE){i,j}); } n*=m; for(int i=0;i<E;i++) e[i].clear(); int d=D.size(), p=P.size(); for(int i=0;i<d;i++) { for(int j=0;j<p;j++) { int t=dis[D[i].x][D[i].y][P[j].x][P[j].y]; if(t!=-1) { // 說明最快t時間可以逃脫 for(int k=t;k<=n;k++) // 則t以上時間都可逃脫 連邊 e[(k-1)*d+i].push_back(n*d+j); } } } if(p==0) { printf("0\n"); return; } int ans=match(d,p); if(ans) printf("%d\n",ans); else printf("impossible\n"); } int main() { int t; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); for(int i=0;i<n;i++) scanf("%s",G[i]); solve(); } return 0; }
View Code