Description
给出一个n*m的网格图,图中有一些障碍节点。
现在有A个男生和B个女生,还有一个小标。
男生要和女生配对,小标可以和任何一个人配对。
每一对CP(雾)只能待在一个点。一个点只能有一对CP。
现在给出A+B+1个人的初始坐标,和他们的移动速度(即移动到4相邻格子所需的时间)
所有人同时移动,求完成配对的最小时间。
Solution
首先,小标的性别完全可以确定,不用管。
然后,我们发现我们要让最长的移动时间最小。
二分答案,判定显然用网络流。
如何建模?
首先,因为每个男生和每个女生都要在一个点,我们不妨把这个点放在中间,左边男生连它,右边女生连他。这里如果有边相连就表示这个人能够在二分上界中到达这个点。
每个点拆点,自己连1.表示这里只能有一对CP。
然后跑最大流就是最多能匹配的CP数,判断一下就好了。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define rep(i,a) for(int i=last[a];i;i=next[i])
#define L 505
#define N 2005
#define M 810005
using namespace std;
typedef long long ll;
const ll inf=1e13;
struct note{int x,y;ll t;}a[L],b[L],q[N];
int n,m,A,B,S,T,l,x,y,dis[N],d[N];
ll dist[L*2][L],time;
int last[N],next[M],t[M],f[M],g[4][2]={0,1,1,0,0,-1,-1,0};
char s[L][L];
void add(int x,int y,int z) {
t[++l]=y;f[l]=z;next[l]=last[x];last[x]=l;
t[++l]=x;f[l]=0;next[l]=last[y];last[y]=l;
}
int get(int x,int y) {return (x-1)*m+y;}
bool bfs() {
memset(dis,0,sizeof(dis));dis[S]=1;
int i=0,j=1;d[1]=S;
while (i<j) rep(k,d[++i])
if (!dis[t[k]]&&f[k]) dis[t[k]]=dis[d[i]]+1,d[++j]=t[k];
return dis[T];
}
int dinic(int x,int y) {
if (x==T) return y;
int now=0;
rep(i,x) if (dis[t[i]]==dis[x]+1&&f[i]) {
int k=dinic(t[i],min(y,f[i]));
f[i]-=k;f[i^1]+=k;y-=k;now+=k;
if (!y) break;
}
if (!now) dis[x]=-1;
return now;
}
bool check(ll x) {
memset(last,0,sizeof(last));l=1;int ans=0;
fo(i,1,A) add(S,i,1);fo(i,1,B) add(i+A+2*n*m,T,1);
fo(i,1,A) fo(j,1,n*m) if (dist[i][j]<=x) add(i,j+A,1);
fo(i,1,B) fo(j,1,n*m) if (dist[i+A][j]<=x) add(j+A+n*m,i+A+2*n*m,1);
fo(i,1,n*m) add(i+A,i+A+n*m,1);
while (bfs()) ans+=dinic(S,0x7fffffff);
if (ans==A) return 1;else return 0;
}
void len(int v,int x,int y,ll t) {
dist[v][get(x,y)]=0;
int i=0,j=1;q[1].x=x;q[1].y=y;
while (i<j) {
x=q[++i].x,y=q[i].y;
fo(k,0,3) {
int xx=x+g[k][0],yy=y+g[k][1],zz=get(xx,yy);
if (xx<1||xx>n||yy<1||yy>m||s[xx][yy]=='#'||dist[v][zz]!=dist[0][0]) continue;
q[++j].x=xx;q[j].y=yy;q[j].t=q[i].t+t;dist[v][zz]=q[j].t;
}
}
}
int main() {
scanf("%d%d%d%d",&n,&m,&A,&B);S=0;T=A+B+2*n*m+2;
if (A==B||abs(A-B)>1) {printf("-1");return 0;}
fo(i,1,n) scanf("%s",s[i]+1);
scanf("%d%d%lld",&x,&y,&time);memset(dist,127,sizeof(dist));
fo(i,1,A) scanf("%d%d%lld",&a[i].x,&a[i].y,&a[i].t);
fo(i,1,B) scanf("%d%d%lld",&b[i].x,&b[i].y,&b[i].t);
if (A<B) a[++A].x=x,a[A].y=y,a[A].t=time;
else b[++B].x=x,b[B].y=y,b[B].t=time;
fo(i,1,A) len(i,a[i].x,a[i].y,a[i].t);
fo(i,1,B) len(i+A,b[i].x,b[i].y,b[i].t);
ll le=1,ri=inf;
while (le<ri) {
ll mid=(le+ri)/2;
if (check(mid)) ri=mid;else le=mid+1;
}
if (le==inf) printf("-1");else printf("%lld",le);
}