题目描述
农场有 个奶牛吃草的区域,有 条路连接一些区域,路是双向的。有些区域有遮雨棚,每个遮雨棚有自己的容量限制
现在要你计算所有牛都能找到地方躲雨最少需要多少时间
题目解析
先用 求一个多源最短路
二分枚举 进行连边,距离小于 ,则连
接着用最大流跑个二分匹配,拆点后,两点之间容量为
到点的容量为 ,点到 的容量为
若最大流量等于奶牛的总量,则这个 可行,继续二分
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 2100;
const int inf = 0x3f3f3f;
int n, m, tot, st[N << 1], cur[N << 1], a[N], b[N];
long long dis[N][N], l, r, maxx = -1, mid, ans, sum = 0;
int s, t, dep[N << 1], q[N << 2];
struct node
{
int last, to;
long long val;
}w[100010];
void add(int u, int v, int ww)
{
w[++tot].to = v;
w[tot].val = ww;
w[tot].last = st[u];
st[u] = tot;
}
void build(long long k)
{
tot = -1;
memset(w, 0, sizeof w);
memset(st, -1, sizeof st);
for (int i = 1; i <= n; i++)
{
if (a[i])
add(s, i, a[i]), add(i, s, 0);
if (b[i])
add(i + n, t, b[i]), add(t, i + n, 0);
}
for (int i = 1; i <= n; i++)
if (a[i] || b[i])
for (int j = 1; j <= n; j++)
if (dis[i][j] <= k && (a[j] || b[j]))
add(i, j + n, inf), add(j + n, i, 0);
}
bool bfs()
{
int ll = 0, rr = 1;
memset(dep, 0, sizeof dep);
memset(q, 0, sizeof q);
dep[s] = 1;
q[rr] = s;
while (ll != rr)
{
int head = q[++ll];
for (int i = st[head]; i != -1; i = w[i].last)
{
if (w[i].val > 0 && !dep[w[i].to])
{
dep[w[i].to] = dep[head] + 1;
q[++rr] = w[i].to;
}
}
}
if (dep[t] > 0)
return true;
return false;
}
long long dfs(int u, long long k)
{
if (u == t) return k;
for (int& i = cur[u]; i != -1; i = w[i].last)
{
if (dep[w[i].to] == dep[u] + 1 && w[i].val > 0)
{
long long p = dfs(w[i].to, min(w[i].val, k));
if (p > 0)
{
w[i].val -= p;
w[i ^ 1].val += p;
return p;
}
}
}
return 0;
}
long long dicnic()
{
long long k = 0;
while (bfs())
{
for (int i = 0; i <= t; i++) cur[i] = st[i];
while (long long p = dfs(s, inf))
k += p;
}
return k;
}
long long find()
{
l = 0, r = (long long)1e12 * 2, ans = -1;
while (l <= r)
{
mid = (l + r) >> 1;
build(mid);
if (sum == dicnic())
ans = mid, r = mid - 1;
else
l = mid + 1;
}
return ans;
}
int main()
{
scanf("%d%d", &n, &m);
s = 0, t = 2 * n + 1;
memset(dis, 0x3f, sizeof dis);
for (int i = 1; i <= n; i++)
{
scanf("%d%d", &a[i], &b[i]);
sum += a[i];
dis[i][i] = dis[i + n][i + n] = 0;
}
for (int i = 1; i <= m; i++)
{
int x, y;
long long z;
scanf("%d%d%lld", &x, &y, &z);
if (dis[x][y] > z)
dis[x][y] = dis[y][x] = z;
maxx += z;
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
for (int k = 1; k <= n; k++)
if (dis[i][k] + dis[k][j] < dis[i][j])
dis[i][j] = dis[j][i] = dis[i][k] + dis[k][j];
printf("%lld\n", find());
return 0;
}