题目大意:给定一个矩阵,'.'表示空位,'X'表示人,'#'表示墙,'@'表示门,每个位置至多只能站一个人,人不能穿越墙,人能从门中出去。每个人每分钟只能上下左右移动一步,问最少需要多少时间让所有的人出去。
思路:
(1):把每个点按照天数拆成d个点。
(2):添加源汇点S和T。
(3):源点向第0天地图上人所在位置的点连一天容量为1的边。
(4):枚举时间Ti
(5):每次枚举只需向残留网络里重新添加一些新的点和边。然后再从源点向汇点跑最大流。
(6):直到前Ti天的最大流总和=总人数时,Ti就是所有的最少天数。
添加新的点和边:
如果当前枚举的天数为d,那么地图中每个位置第d-1天的点向四周和本身第d天的点(新添加的点)连一条容量为1 的边。
如果当前点为出口,则连接在第d天的这个点和出口一条容量为1的边
PS.一开始先用BFS判一下可达性。
My Code:
#include <cstdio> #include <cstring> #include <vector> #include <algorithm> #include <queue> using namespace std; #define maxn 40010 //点数 #define INF 0x7fffffff int N,M,S,T,step,sum,ans; int dir[5][2]={0,1,0,-1,1,0,-1,0,0,0}; struct Edge { int from,to,cap,flow; }; vector<Edge> edges; //边数 edges[e]和edges[e^1]互为反向弧 vector<int> G[maxn]; //G[i][j]表示节点i的第j条边在edges数组中的序号 bool vis[maxn]; //BFS使用 int d[maxn]; //从起点到i的距离 int cur[maxn]; //当前弧的下标 void init() //每组数据运行前记得初始化 { edges.clear(); for(int i = 0; i < maxn; i++)G[i].clear(); } void AddEdge(int from, int to, int cap) //加边,依次是起点、终点、容量 { edges.push_back((Edge){from,to,cap,0}); edges.push_back((Edge){to,from,0,0}); int tmp=edges.size(); G[from].push_back(tmp-2); G[to].push_back(tmp-1); } bool BFS(int s, int t) { memset(vis,0,sizeof(vis)); queue<int> Q; Q.push(s); d[s]=0; vis[s]=1; while(!Q.empty()) { int x=Q.front();Q.pop(); for(int i = 0; i < G[x].size(); i++) { Edge& e=edges[G[x][i]]; if(!vis[e.to]&&e.cap>e.flow) //只考虑残量网络中的弧 { vis[e.to]=1; d[e.to]=d[x]+1; Q.push(e.to); } } } return vis[t]; } int DFS(int x, int a, int t) { if(x==t||a==0)return a; int flow=0,f; for(int& i = cur[x]; i < G[x].size(); i++) //从上次考虑的弧 { Edge& e = edges[G[x][i]]; if(d[x]+1==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow), t))>0) { e.flow+=f; edges[G[x][i]^1].flow-=f; flow+=f; a-=f; if(a==0)break; } } return flow; } int Maxflow(int s, int t) //求最大流,传参:源点,汇点 { int flow=0; while(BFS(s, t)) { memset(cur,0,sizeof(cur)); flow+=DFS(s,INF, t); } return flow; } char data[20][20]; int to(int x, int y){return x*M+y;} void Read() { sum=0;ans=0; S=0;T=maxn-1;step=M*N; for(int i = 1; i <= N; i++)scanf("%s",&data[i][1]); for(int i = 1; i <= N; i++) for(int j = 1; j <= M; j++) if(data[i][j]=='X'){AddEdge(S,to(i,j),1);sum++;} } int out(int x, int y){return (x<1||x>N||y<1||y>M);} int vis2[20][20]; struct node { int x,y; }; int judge(int x, int y) { memset(vis2,0,sizeof(vis2)); queue<node> hehe; node tmp;tmp.x=x;tmp.y=y; hehe.push(tmp); vis2[x][y]=1; while(!hehe.empty()) { tmp=hehe.front(); hehe.pop(); if(data[tmp.x][tmp.y]=='@')return 1; for(int i = 0; i < 4; i++) { int dx=tmp.x+dir[i][0],dy=tmp.y+dir[i][1]; if(!out(dx,dy)&&!vis2[dx][dy]&&data[dx][dy]!='#') { vis2[dx][dy]=1; hehe.push((node){dx,dy}); } } } return 0; } int OK() { for(int i = 1; i <= N; i++) for(int j = 1; j <= M; j++) { if(data[i][j]=='X') { if(!judge(i,j))return 0; } } return 1; } int can(int t) { int Pre=(t-1)*step; int Now=t*step; for(int i = 1; i <= N; i++) for(int j = 1; j <= M; j++) { if(data[i][j]=='#')continue; int u=Pre+to(i,j); for(int k = 0; k < 5; k++) { int dx=i+dir[k][0]; int dy=j+dir[k][1]; if(!out(dx,dy)&&data[dx][dy]!='#') { int v=Now+to(dx,dy); AddEdge(u,v,1); } } if(data[i][j]=='@') { int v=Now+to(i,j); AddEdge(v,T,1); } } int ch=Maxflow(S,T); ans+=ch; return ans==sum; } void solve() { if(sum==0){printf("0\n");return ;} if(!OK()){printf("-1\n");return ;} int Ti=0; while(1) { Ti++; if(can(Ti))break; } printf("%d\n",Ti); } int main() { while(~scanf("%d%d",&N,&M)) { init(); Read(); solve(); } return 0; }