1. 程式人生 > 實用技巧 >Minimum-cost Flow - MCMF - 容量為分數時的處理

Minimum-cost Flow - MCMF - 容量為分數時的處理

傳送門
給出一個無向圖,已知連線情況和每條邊上的費用。
先給出q個查詢,每次查詢規定了每條邊的容量都為\(\frac{u_i}{v_i}\),求出每次查詢時,從源點1到匯點n,流量為1的最小費用值

因為容量一樣,那麼每個增廣路的流量都是\(\frac{u_i}{v_i}\),也就是說需要\(\frac{v_i}{u_i}\)條增廣路,才能使得總流量為1
考慮擴大容量,把容量變成1,也就是說變成了原來的\(\frac{v_i}{u_i}\)倍,那麼用vector記錄每條增廣路的費用,
如果說vec.size() < \(\frac{v_i}{u_i}\),那麼也就是說無法滿足。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
#define ll long long
using namespace std;
const int N = 5e3 + 5;
const int M = 5e4 + 5;
std::vector<int> vec;
struct MCMF{
    struct Edge{
        int to, next, cap, cost; //cap容量, cost單位費用
    }e[M << 1];
    int head[N], tot, n; 
    // dis[] 從源點到各點的最小費用, flow[] 從源點到各點的最小流量, pre[]為每個點的前驅,lats[]為每個點所連的前一個邊
    int dis[N], pre[N], last[N], flow[N];
    bool vis[N];
    void init(int n){
        this->n = n; tot = 1;
        memset(head, 0, sizeof(head));
    }
    void add(int u, int v, int cap, int cost){
        e[++tot].to = v;
        e[tot].cap = cap;
        e[tot].cost = cost;
        e[tot].next = head[u];
        head[u] = tot;
    }
    bool spfa(int s, int t){
        queue <int> q;
        memset(dis, 0x7f, sizeof(dis));
        memset(flow, 0x7f, sizeof(flow));
        memset(vis, 0, sizeof(vis));
        q.push(s); vis[s] = 1; dis[s] = 0; pre[t] = -1;
        
        while (!q.empty()){
            int now = q.front();
            q.pop();
            vis[now] = 0;
            for (int i = head[now]; i; i = e[i].next){
                int v = e[i].to;
                if (e[i].cap > 0 && dis[v] > dis[now] + e[i].cost){
                    dis[v] = dis[now] + e[i].cost;
                    pre[v] = now;
                    last[v] = i;
                    flow[v] = min(flow[now], e[i].cap);
                    if (!vis[v]){
                        vis[e[i].to] = 1;
                        q.push(e[i].to);
                    }
                }
            }
        }
        return pre[t] != -1;
    }
    pair<int,int> mcmf(int s, int t){
        int mincost = 0, maxflow = 0;
        while (spfa(s, t)){ //尋找增廣路
            vec.push_back(dis[t]);
            int now = t;
            maxflow += flow[t];
            mincost += flow[t] * dis[t]; // s到t的最小總費用乘以最小流量
            while (now != s){ //從源點一直回溯到匯點 
                e[last[now]].cap -= flow[t]; // 正向流量減少 
                e[last[now] ^ 1].cap += flow[t]; // 反向流量增加
                now = pre[now];
            }
        }
        return make_pair(mincost, maxflow);
    }
} G;
ll gcd(ll a, ll b){
    return b == 0 ? a : gcd(b, a % b);
}
ll sum[N];
int main(){
    int n, m, s, t;
    while(~scanf("%d%d", &n, &m)){
        G.init(n);
        for (int i = 1; i <= m; i++){
            int u, v, cost;
            scanf("%d%d%d", &u, &v, &cost);
            G.add(u, v, 1, cost); G.add(v, u, 0, -cost); // 建立反向邊,容量為0,負花費 
        }
        vec.clear();
        G.mcmf(1, n);
        sum[0] = 0;
        for(int i = 0; i < vec.size(); i++) {
            sum[i + 1] = sum[i] + vec[i];
        }
        int q;
        scanf("%d", &q);
        while(q--){
            int u, v;
            scanf("%d%d", &u, &v);
            int a = v / u, b = v % u;
            if(1ll * u * vec.size() < v) {
                printf("NaN\n"); continue;
            }
            ll ans = u * sum[a] + 1ll * vec[a] * b;
            ll d = gcd(ans, 1ll * v);
            printf("%lld/%lld\n", ans / d, v / d);
        }
    }
    return 0;
}