2016年青島區域賽 Coding Contest(費用流)
阿新 • • 發佈:2019-02-06
題意:有n個區域,m條邊連線這些區域,這些邊是單向的。現在給出每個區域有多少個選手,多少個午餐。有的區域的選手沒有午餐,所以他們只能去其他區域找午餐。在給定的單向邊上面,有一個流量限制,不能通過超過這個數量的人。而且這些邊上面有網線,只有一個人通過不會對網線有影響,但是有多於1的數量的人通過的時候,多出來的人通過這條邊會有p機率毀壞這條網線。問選手怎樣就餐使得毀壞網路的可能性最小。
解法:題意十分明瞭,一看就是網路流。我們先在選手數目和午餐數目不相等的區域和源點或者匯點建立一條邊。
在此之前我們還要解決一個問題,我們知道的p是毀壞的機率,那麼(1-p)就是不會毀壞的機率,以(1-p)為費用建圖,所有照成影響的選手的機率乘起來就是完全不會有毀壞的機率,假設為q,那麼最終答案就是1-q。但是乘法的話有一個問題,我們在使用費用流去每次改變答案的時候我們是乘以概率的流量次方,用while太慢,老是用pow精度丟失太多,都不是個很好的辦法。所以我們對每個概率取對數10,這樣我們就是化乘法為加法了,最後我們再用一次pow變回來就好了。
所以如果選手數目小於午餐數目,則從此點連一條邊到匯點。反之連一條邊到源點。
然後還要處理一個人是不會對網線有影響的條件,所以我們要拆邊,每條邊都拆成一條流量為1,費用為0的邊和一條流量為f-1,費用為log10(1-p)的邊。
跑一邊費用流即可。
程式碼如下:
#include<bits/stdc++.h> using namespace std; const int INF = 0x3f3f3f3f; const int maxn = 400 + 5; const int maxm = 50000 + 5; const long double eps = 1e-6; int n, m; int head[maxn], to[maxm], front[maxm], flow[maxm], ppp; long double cost[maxm]; long double dis[maxn]; int minflow[maxn]; bool flag[maxn]; int par[maxn], par_line[maxn]; struct MIN_COST_MAX_FLOW{ void init() { memset(head, -1, sizeof(head)); ppp = 0; } bool spfa(int s, int t) { int u, v; fill(dis, dis + t + 1, INF); memset(flag, 0, sizeof(flag)); dis[s] = 0; minflow[s] = INF; queue <int> q; q.push(s); while(!q.empty()) { u = q.front(); q.pop(); flag[u] = 0; for(int i = head[u]; ~i; i = front[i]) { v = to[i]; if(flow[i] > 0 && dis[v] - (dis[u] + cost[i]) > eps) { dis[v] = dis[u] + cost[i]; par[v] = u; par_line[v] = i; minflow[v] = min(minflow[u], flow[i]); if(!flag[v]) { flag[v] = 1; q.push(v); } } } } if(abs(dis[t] - INF) < eps) return 0; return 1; } double slove(int s, int t) { double ans = 0.0; int p; while(spfa(s, t)) { p = t; while(p != s) { flow[par_line[p]] -= minflow[t]; flow[par_line[p]^1] += minflow[t]; p = par[p]; } ans += dis[t] * minflow[t]; } return ans; } void add_edge(int u, int v, int f, long double c) { to[ppp] = v, front[ppp] = head[u], flow[ppp] = f, cost[ppp] = c, head[u] = ppp++; to[ppp] = u, front[ppp] = head[v], flow[ppp] = 0, cost[ppp] = -c, head[v] = ppp++; } }mcmf; int main() { #ifndef ONLINE_JUDGE freopen("in.txt", "r", stdin); #endif int T; cin >> T; while(T--) { mcmf.init(); scanf("%d%d", &n, &m); int s = n + 1, t = s + 1; for(int i = 1; i <= n; i++) { int si, bi; scanf("%d%d", &si, &bi); si -= bi; if(si > 0) { mcmf.add_edge(s, i, si, 0); } else if(si < 0) { mcmf.add_edge(i, t, -si, 0); } } int u, v, f; double c; while(m--) { scanf("%d%d%d%lf", &u, &v, &f, &c); if(f) mcmf.add_edge(u, v, 1, 0); if(f > 1) mcmf.add_edge(u, v, f - 1, -log10(1.0 - c)); } double ans = -mcmf.slove(s, t); printf("%.2f\n", 1.0 - pow(10.0, ans)); } return 0; }