H. Minimum-cost Flow(最小费用流)

Minimum-cost Flow

参考博客

题目🔗

  • 题意:给n个点,m条边的有向图,有q次询问,每次询问给两个数u, v。用来限制每一条边能使用的最大流量为u/v, 问把1单位从1点到n点的最小花费。(所有边容量相同)

  • 题解:

    • 我们可以设所有边费用为c/v,那输入流量和输出流量设为u/v跑一次Dinic就能得到所需最小花费。但是q<=1e5。这么做T飞了。所以最多跑一次Dinic。
    • 为了方便处理,我们同时扩大费用和输入输出流量v倍。所以现在每条边容量为1,费用为c,输入输出流量为u。那我们可以这样想,每次spfa()会增加一条增广路,在跑Dinic时path[i]记录第i+1条增广路的费用。sum[i]表示满流时前i条增广路的总费用。
    • 这样面对每次询问u,v。记录path[],sum[]。由于\(v = a * u +b\) .我们可以在所以增广路径中选择a条满流,一条路径不满流来构成v流量。由于增广路的选择过程本身就从小到大,所以贪心选前a条路径(即0 ~ a-1条增广路)来满流,选第a条增广路来贡献b流量。
    • \(res = sum[a] * u + path[a] * b\)
  • 代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<map>
    #include<queue>
    #include<vector>
    #include<string>
    #include<fstream>
    using namespace std;
    #define rep(i, a, n) for(int i = a; i <= n; ++ i);
    #define per(i, a, n) for(int i = n; i >= a; -- i);
    typedef long long ll;
    const int N = 200100;
    const int mod = 998244353;
    const double Pi = acos(- 1.0);
    const ll INF = 1 << 30;
    const int G = 3, Gi = 332748118;
    ll qpow(ll a, ll b) { ll res = 1; while(b){ if(b) res = (res * a) % mod; a = (a * a) % mod; b >>= 1;} return res; }
    ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
    ll lcm(ll a, ll b) { return a * b / gcd(a, b);}
    bool cmp(int a, int b){ return a > b;}
    //
    
    ll dis[N];
    int head[N], cnt;
    
    ll maxflow = 0, mincost = 0;
    struct node{
        ll to, c, nxt, cost;
    }edge[N * 4];
    
    int n, m, s, t, q;
    int vis[N];
    
    vector<ll> path;
    ll sum[N], res = 0;
    
    
    void add(int u, int v, ll w, ll cost){
        edge[cnt].to = v, edge[cnt].c = w, edge[cnt].cost = cost, edge[cnt].nxt = head[u], head[u] = cnt ++;
        edge[cnt].to = u, edge[cnt].c = 0, edge[cnt].cost =-cost, edge[cnt].nxt = head[v], head[v] = cnt ++;
    }
    
    bool spfa(){
        for(int i = 0; i <= t + 1; ++ i){
            vis[i] = 0;
            dis[i] = INF;
        }
        dis[s] = 0;
        vis[s] = 1;
        queue<int> q;
        q.push(s);
        while(!q.empty()){
            int u = q.front(); q.pop();
            vis[u] = 0;
            for(int i = head[u]; i != -1; i = edge[i].nxt){
                ll v = edge[i].to, w = edge[i].c, costt = edge[i].cost;
                if(w && dis[v] > dis[u] + costt){
                    dis[v] = dis[u] + costt;
                    if(!vis[v]){
                        q.push(v);
                        vis[v] = 1;
                    }
                }
                
            }
        }
        if(dis[t] != INF) return 1;
        else return 0;
    }
    
    ll dfs(int u, ll flow){
        if(u == t){
            vis[t] = 1;
            maxflow += flow;
            return flow;
        }
        ll used = 0, rlow = 0;
        vis[u] = 1;
        for(int i = head[u]; i != -1; i = edge[i].nxt){
            ll v = edge[i].to, w = edge[i].c, costt = edge[i].cost;
            if((!vis[v] || v == t) && w && dis[v] == dis[u] + costt){
                if(rlow = dfs(v, min(flow - used, w))){
                    mincost += costt * rlow;
                    edge[i].c -= rlow;
                    edge[i ^ 1].c += rlow;
                    used += rlow;
                    if(used == flow) break;
                }
            }
        }
        return used;
    }
    
    ll mcmf(){
        path.clear();
        while(spfa()){
            path.push_back(dis[t]);
            vis[t] = 1;
            while(vis[t]){
                memset(vis, 0, sizeof(vis));
                dfs(s, INF);
            }
        }
        return maxflow;
    }
    
    void init(){
        memset(head, -1, sizeof(head));
        maxflow = mincost = 0;
        cnt = 0;
        s = 1, t = n;
    }
    
    int main()
    {
        while(~scanf("%d%d",&n,&m)){
            init();
            while(m --){
                int x, y; ll z; scanf("%d%d%lld",&x, &y, &z);
                add(x, y, 1ll, z);  
            }
            mcmf();
            int pnum = path.size();
            sum[0] = 0;
            for(int i = 0; i < pnum; ++ i) sum[i + 1] = sum[i] + path[i];
            
            scanf("%d",&q);
            while(q --){
                ll u, v; scanf("%lld%lld",&u,&v);
                if(u * pnum < v){
                    printf("NaN\n");
                    continue;
                }
                ll a = v / u, b = v % u;
                res = u * sum[a] + path[a] * b;
                ll g = gcd(res, v);
                res /= g, v /=g;
                printf("%lld/%lld\n",res, v);
            }
        }
        return 0;
    }
    
posted @ 2020-07-24 14:50  A_sc  阅读(287)  评论(0)    收藏  举报