[IndiaHacks 2016 - Online Edition (Div. 1 + Div. 2)] -D. Delivery Bears (二分+最大流)
阿新 • • 發佈:2020-12-26
[IndiaHacks 2016 - Online Edition (Div. 1 + Div. 2)] -D. Delivery Bears (二分+最大流)
題面:
題意:
給定一個含有\(\mathit n\)個節點\(\mathit m\)個有向邊的圖和\(\mathit x\)個工作小熊。保證有一個\(1->n\)的路徑。
現在讓你選擇一個最大的運輸總值,滿足當這\(\mathit x\)個小熊均分任務後選擇一些路徑從\(1->n\),使其可以滿足
每一個有向邊的容量不小於走過該邊的小熊的任務總量。
思路:
顯然可以在區間\([1e(-6),1e6]\)這個區間去二分出每一個小熊最大的運輸任務。
對於check當前二分的數值\(mid\),我們需要用到網路流中的最大流演算法,
在原圖的基礎上將邊權\(c_i\)改為\(c_i/mid\)向下取整的結果,跑最大流演算法,得出\(1->n\)的最大流量\(res\),
判斷\(res\)與\(\mathit x\)的關係即可知道轉移的方向。
我才用ISAP演算法求最大流:該演算法的時間複雜度上限:\(O(E*V^2)\),理想情況:\(O(sqrt(E)*V^2)\)
於是本題的ac程式碼時間複雜度為\(O(m*n^2*log_2x)\)
程式碼:
#include<bits/stdc++.h> using namespace std; #define N 1000 #define INF 1e15 typedef long long ll; struct Edge { int from, to; ll cap, flow; }; struct ISAP { int n, m, s, t; std::vector<Edge> edges; std::vector<int> G[N]; bool vis[N]; int d[N], cur[N]; int p[N], num[N]; void addedge(int from, int to, ll cap) { edges.push_back((Edge) {from, to, cap, 0}); edges.push_back((Edge) {to, from, 0, cap}); int m = edges.size(); G[from].push_back(m - 2); G[to].push_back(m - 1); } void init() { memset(d, 0, sizeof(d)); edges.clear(); for (int i = 0; i <= n; ++i) { G[i].clear(); } } ll Augument() { ll x = t, a = INF; while (x != s) { Edge &e = edges[p[x]]; a = min(a, e.cap - e.flow); x = edges[p[x]].from; } x = t; while (x != s) { edges[p[x]].flow += a; edges[p[x] ^ 1].flow -= a; x = edges[p[x]].from; } return a; } void bfs() { memset(vis, 0, sizeof(vis)); queue<int> q; q.push(t); d[t] = 0; vis[t] = 1; while (!q.empty()) { int x = q.front(); q.pop(); int len = G[x].size(); for (int i = 0; i < len; ++i) { Edge &e = edges[G[x][i]]; if (!vis[e.from] && e.cap > e.flow) { vis[e.from] = 1; d[e.from] = d[x] + 1; q.push(e.from); } } } } ll Maxflow(int s, int t) { this->s = s; this->t = t; ll flow = 0; bfs(); memset(num, 0, sizeof(num)); for (int i = 0; i < n; ++i) { num[d[i]]++; } int x = s; memset(cur, 0, sizeof(cur)); while (d[s] < n) { if (x == t) { flow += Augument(); x = s; } int ok = 0; for (int i = cur[x]; i < G[x].size(); ++i) { Edge &e = edges[G[x][i]]; if (e.cap > e.flow && d[x] == d[e.to] + 1) { ok = 1; p[e.to] = G[x][i]; cur[x] = i; x = e.to; break; } } if (!ok) { int m = n - 1; for (int i = 0; i < G[x].size(); ++i) { Edge &e = edges[G[x][i]]; if (e.cap > e.flow) { m = min(m, d[e.to]); } } if (--num[d[x]] == 0) { break; } num[d[x] = m + 1]++; cur[x] = 0; if (x != s) { x = edges[p[x]].from; } } } return flow; } } gao; int n; int m; int x; int a[N], b[N], c[N]; bool check(double mid) { int S = 1; int T = n; gao.n = T + 1; for (int i = 1; i <= m; ++i) { ll num = floor(1.0 * c[i] / mid); gao.addedge(a[i], b[i], num); } int res = gao.Maxflow(S, T); gao.init(); return res >= x; } int main() { scanf("%d %d %d", &n, &m, &x); for (int i = 1; i <= m; ++i) { scanf("%d %d %d", &a[i], &b[i], &c[i]); } double l = 1e-6; double r = 1000000; double ans, mid; for(int rp=1;rp<=100;++rp) { mid = (l + r) * 0.5; if (check(mid)) { l = mid; ans = mid; } else { r = mid; } } ans*=x; printf("%.6f\n", ans ); return 0; }