gym - 101908G Gasoline (网络流 +二分)
题目大意:
给你n1个加油站(可以看作需求点),n2个炼油厂(供给点),给你m条边,代表一个炼油厂到其中一个加油的站的时间
求能填满所有加油站的最小时间
题目思路:
首先这个问题也不是一眼网络流?,我菜
大意里说了,我们先把两种点分别转化为需求和供给,比较好理解
我们要求最短时间的话,这里肯定是满足单调性的
因为如果五分钟能填满所有加油站,那么我十分钟也一定可以
单调性有了,可以二分了
这里可以直接二分答案也就是时间
怎么check?
首先,我们确定了一个时间后,有些点对关系是用不到的,因为他们的运送时间大于我们的答案,所以我们每次只check有效的部分就ok了
我们建图的时候还需要再转化一下,因为这里给出的点是两种点,都是从1开始编号的
对于第一种点的编号为[1,n1],第二种点的编号我们给重新编号为[1+n1,n2];
现在所有的点都转为一种点了,还需要转化一下,因为题目没有给出明确的点到点的边的容量关系而是给出的每个需求点的需求量和每个供应点的供应量
我们再设置一个源点S和汇点T
S向每个供应点有一条容量为供应点供应量边权的边为容量,建0流的反向边
每个加油站向T有一条需求为边权的边为容量,建0流的反向边
然后跑一边dinic求最大流就ok了
最后只需要求一个最大流是不是等于所有需求点的和就行了
Code:
int n1, n2, m, n, S, T, sum; int p[maxn]; int t[maxn]; struct node { int u, v, w; } edge[50001]; int head[maxn], cnt; struct Node { int u, v, w, next; } e[50001]; void add(int u, int v, int w) { e[cnt].u = u, e[cnt].v = v, e[cnt].w = w; e[cnt].next = head[u], head[u] = cnt++; } int vis[maxn]; int bfs(int st, int en) { queue<int> q; q.push(st); mst(vis, 0); vis[st] = 1; while (q.size()) { int fr = q.front(); q.pop(); for (int i = head[fr]; ~i; i = e[i].next) { int v = e[i].v; if (vis[v] == 0 && e[i].w) { vis[v] = vis[fr] + 1; q.push(v); } } } return vis[en]; } int dfs(int u,int en,int flo) { if(u==en) return flo; int rest = flo; for(int i=head[u];~i;i=e[i].next) { int v = e[i].v; if(e[i].w&&vis[v]==vis[u]+1) { int up = dfs(v,en,min(e[i].w,rest)); rest-=up,e[i].w-=up,e[i^1].w+=up; if(rest==0) break; } } return flo-rest; } int dinic(int st, int en) { int max_flow = 0; while (bfs(st, en) > 0) max_flow += dfs(st, en, inf); return max_flow; } int ok(int x) { cnt = 0; mst(head, -1); for (int i = 1; i <= n2; i++) add(S, i + n1, p[i]), add(i + n1, S, 0); for (int i = 1; i <= n1; i++) add(i, T, t[i]), add(T, i, 0); for (int i = 1; i <= m; i++) { if (edge[i].w > x) continue; int v = edge[i].v; int u = edge[i].u; add(v + n1, u, inf), add(u, v + n1, 0); } int max_flow = dinic(S, T); return max_flow == sum; } int main() { n1 = read(), n2 = read(), m = read(), n = n1 + n2; S = 0, T = n + 1; rep(i, 1, n1) t[i] = read(), sum += t[i]; //加油站 rep(i, 1, n2) p[i] = read(); //炼油场 for (int i = 1; i <= m; i++) edge[i].u = read(), edge[i].v = read(), edge[i].w = read(); int l = 1, r = 1000000, ans = -1; while (l <= r) { int mid = (l + r) >> 1; if (ok(mid)) ans = mid, r = mid - 1; else l = mid + 1; } out(ans); return 0; }