比较难想的一道二分图。
考虑这么建图。
时间和门的二元组为二分图的x集合的元素,每个人为y集合的元素,连边就表示,这段时间内此门能让此人过。
让时间从1开始递增,每次寻找x集合和y集合的最大匹配,当最大匹配等于人数时,此时间满足所有人都离开屋子。
每次找最大匹配可以在上一个时间的残余图上寻找增广路,速度还是挺快的。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
#include<cstdlib>
using namespace std;
#define MAX_V 5000
#define INF 0x3f3f3f3f
typedef pair<int,int> P;
bool vis[MAX_V];
int match[MAX_V];
bool G[MAX_V][MAX_V];
int x_cnt,y_cnt;
bool find_path(int u)
{
for(int i=0;i<y_cnt;i++)
{
if(!vis[i]&&G[u][i])
{
vis[i]=1;
if(match[i]==-1||find_path(match[i]))
{
match[i]=u;
return true;
}
}
}
return false;
}
int N,M;
char MAP[20][20];
int DIS[20][20][20][20];
int dx[4]={1,0,-1,0};
int dy[4]={0,1,0,-1};
vector<P> DOOR;
vector<P> MAN;
void bfs(int u,int v,int d[20][20])
{
queue<P> Q;
d[u][v]=0;
Q.push(P(u,v));
while(!Q.empty())
{
P now=Q.front(); Q.pop();
for(int i=0;i<4;i++)
{
int tx=now.first+dx[i],ty=now.second+dy[i];
if(tx>=0&&tx<N&&ty>=0&&ty<M&&MAP[tx][ty]=='.'&&d[tx][ty]==-1)
{
d[tx][ty]=d[now.first][now.second]+1;
Q.push(P(tx,ty));
}
}
}
}
int slove()
{
memset(G,0,sizeof(G));
memset(match,-1,sizeof(match));
x_cnt=DOOR.size();
y_cnt=MAN.size();
int res=0;
for(int T=0;T<N*M;T++)
{
for(int i=0;i<DOOR.size();i++)
{
for(int j=0;j<MAN.size();j++)
{
int doorx=DOOR[i].first,doory=DOOR[i].second;
int manx=MAN[j].first,many=MAN[j].second;
if(DIS[doorx][doory][manx][many]!=-1&&DIS[doorx][doory][manx][many]<=T+1&&G[i][j]==0) { G[i][j]=1; }
}
}
for(int i=0;i<x_cnt;i++)
{
memset(vis,0,sizeof(vis));
if(find_path(i)) res++;
}
if(res==MAN.size()) return T+1;
}
return -1;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
DOOR.clear();
MAN.clear();
scanf("%d%d",&N,&M);
for(int i=0;i<N;i++)
{
scanf("%s",MAP[i]);
}
memset(DIS,-1,sizeof(DIS));
for(int i=0;i<N;i++)
{
for(int j=0;j<M;j++)
{
if(MAP[i][j]=='D')
{
DOOR.push_back(P(i,j));
bfs(i,j,DIS[i][j]);
}
else if(MAP[i][j]=='.') MAN.push_back(P(i,j));
}
}
int ans=slove();
if(ans==-1) printf("impossible\n");
else printf("%d\n",ans);
}
return 0;
}