首先对每一个块空地进行bfs,预处理出每一块空地距离到达每一扇门的距离;然后二分枚举答案,通过跑最大流来验证。
注意bfs时的初始化与省略小小的剪枝;
注意数组的大小。
#include <bits/stdc++.h>
using namespace std;
const int N=21;
const int dx[4]={0,-1,0,1},dy[4]={-1,0,1,0};
int n,m,tot,size1,size2;
int id[N][N],d[N*N][N*N],a[N*N],b[N*N];
bool vis[N][N];
char str[N][N];
struct node{int x,y,z;};
queue<node>q;
inline void init(int x,int y)
{
memset(vis,false,sizeof(vis));
int now=id[x][y];
q.push((node){x,y,0}); vis[x][y]=true; d[now][id[x][y]]=0;
while (q.size())
{
node u=q.front(); q.pop();
for (register int i=0; i<4; ++i)
{
int xx=u.x+dx[i],yy=u.y+dy[i];
if (xx<1 || xx>n || yy<1 || yy>m) continue;
if (str[xx][yy]=='X' || vis[xx][yy]) continue;
vis[xx][yy]=true;
if (str[xx][yy]=='.') q.push((node){xx,yy,u.z+1});
else d[now][id[xx][yy]]=u.z+1;
}
}
}
const int M=2e5+5,MM=1e7+5;
int s,t;
int dep[M];
int cnt,head[M];
struct edge{int next,to,w;}e[MM];
inline void add(int u,int v,int w)
{
cnt++;
e[cnt].next=head[u];
e[cnt].to=v;
e[cnt].w=w;
head[u]=cnt;
}
queue<int>q1;
inline bool bfs()
{
memset(dep,-1,sizeof(dep));
dep[s]=0; q1.push(s);
while (q1.size())
{
int u=q1.front(); q1.pop();
for (register int i=head[u]; i; i=e[i].next)
if (e[i].w && dep[e[i].to]==-1)
{
dep[e[i].to]=dep[u]+1;
q1.push(e[i].to);
}
}
if (dep[t]==-1) return false;
else return true;
}
int dfs(int u,int flow)
{
if (u==t) return flow;
int remain=flow;
for (register int i=head[u]; i; i=e[i].next)
if (e[i].w && dep[u]+1==dep[e[i].to])
{
int k=dfs(e[i].to,min(remain,e[i].w));
if (!k) {dep[e[i].to]=-1; continue;}
remain-=k; e[i].w-=k; e[i^1].w+=k;
if (!remain) break;
}
return flow-remain;
}
inline int dinic()
{
int ans=0;
while (bfs()) ans+=dfs(s,2e9);
return ans;
}
inline bool jay(int mid)
{
cnt=1; memset(head,0,sizeof(head));
s=0; t=size1+size2*mid+1;
for (register int i=1; i<=size1; ++i) add(s,i,1),add(i,s,0);
for (register int i=1; i<=size1; ++i)
for (register int j=1; j<=size2; ++j)
for (register int k=d[a[i]][b[j]]; k<=mid; ++k) add(i,size1+(j-1)*mid+k,1),add(size1+(j-1)*mid+k,i,0);
for (register int i=size1+1; i<=size1+size2*mid; ++i) add(i,t,1),add(t,i,0);
if (dinic()>=size1) return true;
else return false;
}
int main(){
memset(d,60,sizeof(d));
scanf("%d%d",&n,&m);
for (register int i=1; i<=n; ++i) scanf("%s",str[i]+1);
for (register int i=1; i<=n; ++i)
for (register int j=1; j<=m; ++j)
{
id[i][j]=++tot;
if (str[i][j]=='.') a[++size1]=id[i][j];
if (str[i][j]=='D') b[++size2]=id[i][j];
}
for (register int i=1; i<=n; ++i)
for (register int j=1; j<=m; ++j) if (str[i][j]=='.') init(i,j);
int l,r,mid,ans;
l=0; r=n*m; ans=-1;
while (l<=r)
{
mid=l+r>>1;
if (jay(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
if (ans==-1) puts("impossible");
else printf("%d\n",ans);
return 0;
}
看了评测记录以后发现自己的代码挺慢,后来发现是由于我对每一块空地都做了一次bfs,如果对每一扇门都做一次bfs,可以得到同样的效果,且对于极限数据来讲,门的数量比空地的数量少很多。
所以对门做bfs可以快很多。
BZOJ上的记录,上面的是对门做bfs,下面的是原来对空地做bfs。
#include <bits/stdc++.h>
using namespace std;
const int N=21;
const int dx[4]={0,-1,0,1},dy[4]={-1,0,1,0};
int n,m,tot,size1,size2;
int id[N][N],d[N*N][N*N],a[N*N],b[N*N];
bool vis[N][N];
char str[N][N];
struct node{int x,y,z;};
queue<node>q;
inline void init(int x,int y)
{
memset(vis,false,sizeof(vis));
int now=id[x][y];
q.push((node){x,y,0}); vis[x][y]=true; d[id[x][y]][now]=0;
while (q.size())
{
node u=q.front(); q.pop();
for (register int i=0; i<4; ++i)
{
int xx=u.x+dx[i],yy=u.y+dy[i];
if (xx<1 || xx>n || yy<1 || yy>m) continue;
if (str[xx][yy]!='.' || vis[xx][yy]) continue;
vis[xx][yy]=true;
q.push((node){xx,yy,u.z+1});
d[id[xx][yy]][now]=u.z+1;
}
}
}
const int M=2e5+5,MM=1e7+5;
int s,t;
int dep[M];
int cnt,head[M];
struct edge{int next,to,w;}e[MM];
inline void add(int u,int v,int w)
{
cnt++;
e[cnt].next=head[u];
e[cnt].to=v;
e[cnt].w=w;
head[u]=cnt;
}
queue<int>q1;
inline bool bfs()
{
memset(dep,-1,sizeof(dep));
dep[s]=0; q1.push(s);
while (q1.size())
{
int u=q1.front(); q1.pop();
for (register int i=head[u]; i; i=e[i].next)
if (e[i].w && dep[e[i].to]==-1)
{
dep[e[i].to]=dep[u]+1;
q1.push(e[i].to);
}
}
if (dep[t]==-1) return false;
else return true;
}
int dfs(int u,int flow)
{
if (u==t) return flow;
int remain=flow;
for (register int i=head[u]; i; i=e[i].next)
if (e[i].w && dep[u]+1==dep[e[i].to])
{
int k=dfs(e[i].to,min(remain,e[i].w));
if (!k) {dep[e[i].to]=-1; continue;}
remain-=k; e[i].w-=k; e[i^1].w+=k;
if (!remain) break;
}
return flow-remain;
}
inline int dinic()
{
int ans=0;
while (bfs()) ans+=dfs(s,2e9);
return ans;
}
inline bool jay(int mid)
{
cnt=1; memset(head,0,sizeof(head));
s=0; t=size1+size2*mid+1;
for (register int i=1; i<=size1; ++i) add(s,i,1),add(i,s,0);
for (register int i=1; i<=size1; ++i)
for (register int j=1; j<=size2; ++j)
for (register int k=d[a[i]][b[j]]; k<=mid; ++k) add(i,size1+(j-1)*mid+k,1),add(size1+(j-1)*mid+k,i,0);
for (register int i=size1+1; i<=size1+size2*mid; ++i) add(i,t,1),add(t,i,0);
if (dinic()>=size1) return true;
else return false;
}
int main(){
memset(d,60,sizeof(d));
scanf("%d%d",&n,&m);
for (register int i=1; i<=n; ++i) scanf("%s",str[i]+1);
for (register int i=1; i<=n; ++i)
for (register int j=1; j<=m; ++j)
{
id[i][j]=++tot;
if (str[i][j]=='.') a[++size1]=id[i][j];
if (str[i][j]=='D') b[++size2]=id[i][j];
}
for (register int i=1; i<=n; ++i)
for (register int j=1; j<=m; ++j) if (str[i][j]=='D') init(i,j);
int l,r,mid,ans;
l=0; r=n*m; ans=-1;
while (l<=r)
{
mid=l+r>>1;
if (jay(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
if (ans==-1) puts("impossible");
else printf("%d\n",ans);
return 0;
}