2020牛客暑期多校訓練營(第一場)H-Minimum-cost Flow(最小費用流)
阿新 • • 發佈:2020-07-20
題目連結
題目大意:初始給定一個\(n\)個點,\(m\)條邊,費用全部確定的網路。要求對於接下來的\(k\)次詢問,每次都給定所有邊的容量為一個分數\(\frac{u}{v}\)。要求對於每一個詢問計算從點\(1\)到點\(n\)跑大小為\(1\)的流的最小花費。
大致思路:因為我們事先知道了所有邊的費用,並且後續所有邊的最大容量全部相同。那麼初始建圖的時候所有邊的最大容量都設定為\(1\),這樣我們只需要跑一邊最小費用最大流,並且記錄每一次的增廣路徑的花費(由於容量為\(1\),所以最少花費就是最短路的距離)。然後對於每一次詢問我們進行分析:由於容量給定的是一個分數,所以我們先將所有邊的容量擴大至\(v\)
#include <bits/stdc++.h> using namespace std; typedef long long ll; class MinCostFlow { public: struct Result { int flow, cost; }; MinCostFlow(int n, int m = 0) : visited(n), head(n, -1), dist(n), prev(n) ,flow(n), preu(n){ edges.reserve(m << 1); } void add_edge(int u, int v, int capacity, int cost) { internal_add_edge(u, v, capacity, cost); internal_add_edge(v, u, 0, -cost); } bool spfa(int src, int dst) { const int infdist = std::numeric_limits<int>::max(); std::fill(dist.begin(), dist.end(), infdist); std::fill(flow.begin(), flow.end(), infdist); dist[src] = 0; std::queue<int> queue; queue.push(src); while (!queue.empty()) { int u = queue.front(); queue.pop(); visited[u] = false; for (int iter = head[u]; ~iter;) { int v = edges[iter].v; if (edges[iter].rest && dist[u] + edges[iter].cost < dist[v]) { dist[v] = dist[u] + edges[iter].cost;//費用增廣路的最短路 prev[v] = iter;//上一邊 preu[v] = u;//前驅點 flow[v] = min(flow[u],edges[iter].rest);//增廣的瓶頸流量 if (!visited[v]) { visited[v] = true; queue.push(v); } } iter = edges[iter].next; } } return dist[dst]!=infdist; } vector<int> ans; Result MCMF(int src,int dst){ int maxflow=0,mincost=0; while(spfa(src,dst)){ int v=dst; ans.push_back(dist[dst]); maxflow+=flow[dst]; mincost+=flow[dst]*dist[dst]; while(v!=src){ edges[prev[v]].rest-=flow[dst]; edges[prev[v]^1].rest+=flow[dst]; v=preu[v]; } } return Result{maxflow,mincost}; } private: struct Edge { int v, next, rest, cost;//終點,下條邊,容量,花費 }; void internal_add_edge(int u, int v, int capacity, int cost) { edges.push_back(Edge{v, head[u], capacity, cost}); head[u] = edges.size() - 1; } std::vector<bool> visited; std::vector<int> head, dist, prev, flow, preu; std::vector<Edge> edges; }; int main(){ int n,m; while(cin>>n>>m){ MinCostFlow net(n,m); for(int i=0,a,b,c;i<m;i++){ cin>>a>>b>>c; net.add_edge(a-1,b-1,1,c); } auto res=net.MCMF(0,n-1); int q; cin>>q; while(q--){ int u,v; scanf("%d%d",&u,&v); if(u==0)puts("NaN"); else{ ll fz=0,fm=v; for(int an : net.ans){ if(v>u)fz+=1ll*an*u,v-=u; else {fz+=1ll*an*v,v=0;break;} } if(v)puts("NaN"); else{ ll gd=__gcd(fz,fm); printf("%lld/%lld\n",fz/gd,fm/gd); } } } } return 0; }