1. 程式人生 > 實用技巧 >2020牛客暑期多校訓練營(第一場)H-Minimum-cost Flow(最小費用流)

2020牛客暑期多校訓練營(第一場)H-Minimum-cost Flow(最小費用流)

題目連結

題目大意:初始給定一個\(n\)個點,\(m\)條邊,費用全部確定的網路。要求對於接下來的\(k\)次詢問,每次都給定所有邊的容量為一個分數\(\frac{u}{v}\)。要求對於每一個詢問計算從點\(1\)到點\(n\)跑大小為\(1\)的流的最小花費。
大致思路:因為我們事先知道了所有邊的費用,並且後續所有邊的最大容量全部相同。那麼初始建圖的時候所有邊的最大容量都設定為\(1\),這樣我們只需要跑一邊最小費用最大流,並且記錄每一次的增廣路徑的花費(由於容量為\(1\),所以最少花費就是最短路的距離)。然後對於每一次詢問我們進行分析:由於容量給定的是一個分數,所以我們先將所有邊的容量擴大至\(v\)

倍,那麼所有邊的最大容量就變成了\(u\)。所以現在我們要大小為\(v\)的流,最後得出的花費除以\(v\)就是最終所求的答案。由於所有的最大容量都為\(u\),所以每一次的增廣所增加的流量都為\(u\)。最後一次把剩餘的流量補全即可。如果最大流無法達到\(v\),就輸出\(NaN\)。具體看程式碼實現。

#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;
}