二分+最大流。
首先可以发现如果t时间可以全部撤离,那么所有大于t的时间都可以撤离,于是考虑二分。
又发现其实一个人往一个门走,走的肯定是最短路,于是可以计算出每个人到每个门的最短路。
每一个人看作一个流量,由源点向每个人连容量为1的边。将每个门拆点表示每个时间出去的人(对于一个门C可以拆成编号为C+1,C+2,C+3,C+4……都向汇点连容量1的边),最多拆400个。每个人向每个门的【C+最短路距离】点连一条容量为1的边。对于每个C+i,向C+i+1连容量INF的边,表示把某一个人安排到下一秒。于是看最大流是否等于人数。
毕竟这题不卡内存,数组就乱开了- -
#include
#include
#include
#include
#include
#define C 450
#define N 22
using namespace std;
vector<int> point[10000];
char s[N];
struct node{int x,y;};
const int S = 1, T = 2, INF = 1<<29, dx[4]={0,0,1,-1}, dy[4]={1,-1,0,0};
int map[N][N], last[200000], level[200000], n, m;
int cnt=1, dis[N][N], beg[N][N], tot=0, len[500000];
bool ban[500000];
struct edge{int next,to,flow;}e[500000];
int pack(int i, int j){return i*21+j;};
void inser(int a, int b, int c)
{
e[++cnt]=(edge){last[a],b,c};
last[a]=cnt;
e[++cnt]=(edge){last[b],a,0};
last[b]=cnt;
}
void pre(int xx, int yy)
{
queue q;
q.push((node){xx,yy});
memset(dis,-1,sizeof(dis));
dis[xx][yy]=0;
while(!q.empty())
{
int x=q.front().x, y=q.front().y;
q.pop();
if(x!=xx||y!=yy)len[cnt+1]=len[cnt+2]=dis[x][y],inser(pack(x,y),pack(xx,yy)*C+dis[x][y],1);
for(int i = 0; i < 4; i++)
{
int nx=x+dx[i], ny=y+dy[i];
if(nx<1||ny<1||nx>n||ny>m)continue;
if(dis[nx][ny]==-1 && map[nx][ny]==1)
{
q.push((node){nx,ny});
dis[nx][ny]=dis[x][y]+1;
}
}
}
}
bool bfs()
{
memset(level,0,sizeof(level));
queue<int> q;
q.push(S);
level[S]=1;
while(!q.empty())
{
int x=q.front();
q.pop();
if(x==T)return 1;
for(int i = last[x]; i; i=e[i].next)
{
int y=e[i].to;
if(ban[i] || ban[i^1])continue;
if(!level[y] && e[i].flow!=0)
{
level[y]=level[x]+1;
q.push(y);
}
}
}
return 0;
}
int dfs(int x, int flow)
{
if(x==T)return flow;
int use=0;
for(int i = last[x]; i; i=e[i].next)
{
int y=e[i].to;
if(level[y]!=level[x]+1 || ban[i] || ban[i^1])continue;
int w=dfs(y,min(flow-use,e[i].flow));
use+=w;
e[i].flow-=w;
e[i^1].flow+=w;
if(use==flow)return use;
}
return use;
}
bool check(int time)
{
memset(ban,0,sizeof(ban));
for(int i = 2; i <= cnt; i+=2)
{
e[i].flow+=e[i^1].flow;
e[i^1].flow=0;
}
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
if(map[i][j]==-1)
{
int tmp=pack(i,j);
for(int k = time; k < 400; k++)
ban[point[tmp][k]]=ban[point[tmp][k]]=1;
}
for(int i = 2; i <= cnt; i++)
if(len[i]>time)
ban[i]=ban[i^1]=1;
int ret=0;
while(bfs())
{
ret+=dfs(S,INF);
}
return ret==tot;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++)
{
scanf("%s",s+1);
for(int j = 1; j <= m; j++)
{
if(s[j]=='.')
{
map[i][j]=1;
inser(S,pack(i,j),1);
tot++;
}
else if(s[j]=='D')
{
map[i][j]=-1;
for(int k = 1; k <= 400; k++)
{
point[pack(i,j)].push_back(cnt+1);
inser(pack(i,j)*C+k,T,1);
inser(pack(i,j)*C+k,pack(i,j)*C+k+1,INF);
}
}
else map[i][j]=0;
}
}
for(int i = 1; i <= n; i++)
for(int j = 1 ; j <= m; j++)
if(map[i][j]==-1)
pre(i,j);
int l = 0, r = 400;
while(lint mid=(l+r)>>1;
if(check(mid))r=mid;
else l=mid+1;
}
if(l==400)printf("impossible\n");
else printf("%d\n",l);
}