题目链接:点击打开链接

题意:

给定n*m的地图 a个男人 b个女人

下面n*m的方格图.表示空地 *表示障碍。

下面第一行给出中性人的坐标和移动时间

下面a行给出每个男人的坐标和移动时间

下面b行给出女人的坐标和移动时间。

移动时间是指人移动到相邻矩阵的时间(人是不能走到障碍物上的)

每个空地上恰好有一间房子(一间房子只能住一对夫妇,住了人的空地别人还是可以走过的)。

目标:使得所有人都结成夫妇且住在房子里(中性人可以和男的结成夫妇,也可以和女的结成夫妇)

所花的最大时间最小,即最后一个人住进房子的时间最小。


思路:

假设没有变性人,则这题就是一个经典建图了,

二分最大时间,然后建图即可。

设now为每个人允许运动的最大时间

男人连源点 flow = 1

男人连能走到的房子 flow = 1 (所谓能走到就是所花时间<=now)

房子连女人 flow = 1

女人连汇点 flow = 1

因为一个房子只能住一个人,所以房子拆点一下,限流为1

而对于变性人其实并不是任意匹配的,当且仅当 abs(男人数量-女人数量)=1时才能找到解,即一开始就能确定变性人的性别。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<queue>
#include<cmath>
template <class T>
inline bool rd(T &ret) {
	char c; int sgn;
	if (c = getchar(), c == EOF) return 0;
	while (c != '-' && (c<'0' || c>'9')) c = getchar();
	sgn = (c == '-') ? -1 : 1;
	ret = (c == '-') ? 0 : (c - '0');
	while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0');
	ret *= sgn;
	return 1;
}
template <class T>
inline void pt(T x) {
	if (x <0) {
		putchar('-');
		x = -x;
	}
	if (x>9) pt(x / 10);
	putchar(x % 10 + '0');
}
using namespace std;
typedef long long ll;
const ll inf = 1e10;
const int N = 10019;
const int M = 10000000;
template<class T>
struct Max_Flow {
	int n;
	int Q[N], sign;
	int head[N], level[N], cur[N], pre[N];
	int nxt[M], pnt[M], E;
	T cap[M];
	void Init(int n) {
		this->n = n + 1;
		E = 0;
		std::fill(head, head + this->n, -1);
	}
	//有向rw 就= 0  
	void add(int from, int to, T c, T rw) {
		pnt[E] = to;
		cap[E] = c;
		nxt[E] = head[from];
		head[from] = E++;

		pnt[E] = from;
		cap[E] = rw;
		nxt[E] = head[to];
		head[to] = E++;
	}
	bool Bfs(int s, int t) {
		sign = t;
		std::fill(level, level + n, -1);
		int *front = Q, *tail = Q;
		*tail++ = t; level[t] = 0;
		while (front < tail && level[s] == -1) {
			int u = *front++;
			for (int e = head[u]; e != -1; e = nxt[e]) {
				if (cap[e ^ 1] > 0 && level[pnt[e]] < 0) {
					level[pnt[e]] = level[u] + 1;
					*tail++ = pnt[e];
				}
			}
		}
		return level[s] != -1;
	}
	void Push(int t, T &flow) {
		T mi = inf;
		int p = pre[t];
		for (int p = pre[t]; p != -1; p = pre[pnt[p ^ 1]]) {
			mi = std::min(mi, cap[p]);                                                                                                                                       
		}
		for (int p = pre[t]; p != -1; p = pre[pnt[p ^ 1]]) {
			cap[p] -= mi;
			if (!cap[p]) {
				sign = pnt[p ^ 1];
			}
			cap[p ^ 1] += mi;
		}
		flow += mi;
	}
	void Dfs(int u, int t, T &flow) {
		if (u == t) {
			Push(t, flow);
			return;
		}
		for (int &e = cur[u]; e != -1; e = nxt[e]) {
			if (cap[e] > 0 && level[u] - 1 == level[pnt[e]]) {
				pre[pnt[e]] = e;
				Dfs(pnt[e], t, flow);
				if (level[sign] > level[u]) {
					return;
				}
				sign = t;
			}
		}
	}
	T Dinic(int s, int t) {
		pre[s] = -1;
		T flow = 0;
		while (Bfs(s, t)) {
			std::copy(head, head + n, cur);
			Dfs(s, t, flow);
		}
		return flow;
	}
};
Max_Flow <ll>F;
struct node{
	int x, y; ll t;
	node(int a = 0, int b = 0, ll c = 0) :x(a), y(b), t(c){}
}AA[N], B[N], C;
int n, m, a, b;
char mp[24][24];
ll Dis[24][24][24][24];
int has1(int x){ return x - 1; }
int has2(int x, int y){ return a + (x - 1)*m + y - 1; }
int has3(int x, int y){ return a + n*m + (x - 1)*m + y - 1; }
int has4(int x){ return a + 2 * n*m + x - 1; }
bool ok(ll now){
	int from = has4(b) + 1, to = from + 1;
	F.Init(to+10);
	for (int i = 1; i <= a; i++){
		F.add(from, has1(i), 1, 0);
		for (int x = 1; x <= n; x++)
		for (int y = 1; y <= m; y++)
			if (Dis[x][y][AA[i].x][AA[i].y] < inf && Dis[x][y][AA[i].x][AA[i].y] * AA[i].t <= now)
				F.add(has1(i), has2(x, y), 1, 0);
	}
	for (int i = 1; i <= n; i++)for (int j = 1; j <= m; j++)if (mp[i][j] == '.') F.add(has2(i, j), has3(i, j), 1, 0);
	for (int i = 1; i <= b; i++){
		F.add(has4(i), to, 1, 0);
		for (int x = 1; x <= n; x++)
		for (int y = 1; y <= m; y++)
			if (Dis[x][y][B[i].x][B[i].y] < inf && Dis[x][y][B[i].x][B[i].y] * B[i].t <= now)
				F.add(has3(x, y), has4(i), 1, 0);
	}
	return F.Dinic(from, to) == b;
}
ll solve(){
	if (a != b)return -1;
	ll ans = -1, l = 0, r = 1e18;
	while (l <= r){
		ll mid = (l + r) >> 1;
		if (ok(mid)){
			r = mid - 1;
			ans = mid;
		}
		else
			l = mid + 1;
	}
	return ans;
}
void pre(){
	for (int i = 1; i <= n; i++)for (int j = 1; j <= m; j++)for (int x = 1; x <= n; x++)for (int y = 1; y <= m; y++)Dis[i][j][x][y] = inf;
	int step[4][2] = { 0, 1, 0, -1, 1, 0, -1, 0 };
	for (int i = 1; i <= n; i++)
	for (int j = 1; j <= m; j++){
		if (mp[i][j] == '*')continue;
		Dis[i][j][i][j] = 0;
		for (int k = 0; k < 4; k++){
			int x = i + step[k][0], y = j + step[k][1];
			if (1 <= x && x <= n && 1 <= y&&y <= m && mp[x][y] == '.')
				Dis[i][j][x][y] = 1;
		}
	}
	for (int x1 = 1; x1 <= n; x1++)for (int y1 = 1; y1 <= m; y1++) if (mp[x1][y1] == '.')
	for (int x2 = 1; x2 <= n; x2++)for (int y2 = 1; y2 <= m; y2++) if (mp[x2][y2]=='.' && Dis[x2][y2][x1][y1]<inf)
	for (int x3 = 1; x3 <= n; x3++)for (int y3 = 1; y3 <= m; y3++) if (mp[x3][y3] == '.')
		Dis[x2][y2][x3][y3] = min(Dis[x2][y2][x3][y3], Dis[x2][y2][x1][y1] + Dis[x1][y1][x3][y3]);
	if (a>b)B[++b] = C;
	else AA[++a] = C;
}
void input(){
	rd(n); rd(m); rd(a); rd(b);
	for (int i = 1; i <= n; i++)scanf("%s", mp[i] + 1);
	rd(C.x); rd(C.y); rd(C.t);
	for (int i = 1; i <= a; i++) { rd(AA[i].x); rd(AA[i].y); rd(AA[i].t); }
	for (int i = 1; i <= b; i++) { rd(B[i].x); rd(B[i].y); rd(B[i].t); }
}

int main(){
	input();
	pre();
	cout<<solve()<<endl;
	return 0;
}