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

SummerMingQAQ發表於2020-07-14

Minimum-cost Flow

題目:給n個點,m條邊。接下來m行包含(a,b,c),即a,b之間有單位流量代價為c的邊。接下來有q個問題,每個問題給定(x,y),即假設每條邊的容量為x/y時,從點1到點n流1單位的流量,最少的花費是多少,如果無法從點1到點n流1單位的流量則輸出“NaN”。

思路:首先我們需要想到一個結論,每條邊最多隻能使用一次,這個自己比劃一下就可以,其中包含貪心的想法。得到這個結論後,我們發現整張圖就變成了流量為1的網路流。然後,根據(x,y)說明我們至少需要 y / x + (y % x != 0)條從1到n的最少花費路徑,這個我們可以通過最小費用流預處理得到所有能夠得到的最小花費路徑,並記錄每條路徑的最小花費。這樣,對於每個詢問(x,y),我們只需要取前y / x小的邊,如果y % x != 0,則從後面的邊補充一些即可。

補充:為什麼我們可以直接這樣直接取路徑,看上圖,我們得到的最小花費應該是兩個:3,21。這是懂得網路流演算法都能看出的,如果我們有組詢問(2,3),我們可以得到答案應該是ans = 3 * 2/3 + 21 * 1/3 = 9。但這兩條路分別是1->2->3->4和1->3->2->4得到的,會感覺有點奇怪“這樣就是答案”。其實我們需要回到網路流的反向邊在演算法中的用途"反流"和正向邊可以把"反流"失去的流量再"補流",下面附一個來體現ans = 9是怎麼得到的,然後思考。

 

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <algorithm>
  4 #include <queue>
  5 #include <vector>
  6 #include <cstring>
  7 
  8 using namespace std;
  9 
 10 #define ll long long
 11 #define pb push_back
 12 #define fi first
 13 #define se second
 14 
 15 const int N = 100;
 16 const int M = 200;
 17 const int INF = 1e9 + 7e8;
 18 struct edge
 19 {
 20     int to, nxt, cap, flow, w;
 21 }e[M << 1];
 22 int head[N], d[N], vis[N], pre[N];
 23 queue<int > que;
 24 int  a[N], b[N];
 25 int n, m, tot, s, t, cnt;
 26 
 27 inline void add(int u, int v, int w)
 28 {
 29     e[tot].to = v; e[tot].w = w; e[tot].cap = 1;
 30     e[tot].flow = 0; e[tot].nxt = head[u]; head[u] = tot++;
 31     e[tot].to = u; e[tot].w = -w; e[tot].cap = 0;
 32     e[tot].flow = 0; e[tot].nxt = head[v]; head[v] = tot++;
 33 }
 34 
 35 bool spfa()
 36 {
 37     for(int i = 1; i <= n; ++i) d[i] = INF, vis[i] = false, pre[i] = -1;
 38     while(!que.empty()) que.pop();
 39     d[s] = 0; vis[s] = true; pre[s] = -1;
 40     que.push(s);
 41     //printf("s = %d  t = %d\n", s, t);
 42     while(!que.empty()){
 43         int now = que.front();
 44         que.pop();
 45         vis[now] = false;
 46     
 47         for(int o = head[now]; ~o; o = e[o].nxt){
 48             if(e[o].cap - e[o].flow && d[e[o].to] > d[now] + e[o].w){
 49                 d[e[o].to] = d[now] + e[o].w;
 50                 pre[e[o].to] = o;
 51                 if(!vis[e[o].to]){
 52                     vis[e[o].to] = true;
 53                     que.push(e[o].to);
 54                 }
 55             }
 56         }
 57     }
 58     if(pre[t] == -1) return false;
 59     else return true;
 60 }
 61 
 62 void mcmf()
 63 {
 64     while(spfa()){
 65         int _min = INF;
 66         for(int o = pre[t]; ~o; o = pre[e[o ^ 1].to]){
 67             _min = min(_min, e[o].cap - e[o].flow);
 68         }
 69         for(int o = pre[t]; ~o; o = pre[e[o ^ 1].to]){
 70             e[o].flow += _min;
 71             e[o ^ 1].flow -= _min;
 72         }
 73         //cout << _min << endl;
 74         //cout << "dis = " << d[t] << endl;
 75         a[++cnt] = d[t];
 76         //cout << "d = " << d[t] << endl;
 77     }
 78     for(int i = 1; i <= cnt; ++i) a[i] += a[i - 1];
 79 }
 80 
 81 ll GCD(ll a, ll b)
 82 {
 83     return b == 0 ? a : GCD(b, a % b);
 84 }
 85 
 86 void solve()
 87 {
 88     while(~scanf("%d%d", &n, &m)){
 89         cnt = 0;
 90         for(int i = 0; i <= n; ++i) head[i] = -1; tot = 0;
 91         int x, y, w;
 92         for(int i = 1; i <= m; ++i){
 93             scanf("%d%d%d", &x, &y, &w);
 94             add(x, y, w);
 95         }
 96         s = 1; t = n;
 97         mcmf();
 98         
 99         int q;
100         scanf("%d", &q);
101         while(q--){
102             int x, y;
103             scanf("%d%d", &x, &y);
104             if(x == 0){
105                 puts("NaN");
106                 continue;
107             }
108             int gcd = GCD(x, y);
109             y /= gcd; x /= gcd;
110 
111             int need = y / x + (y % x != 0);
112             if(need > cnt){
113                 puts("NaN");
114                 continue;       
115             }
116 
117             need = y / x;
118             ll up = (ll)a[need] * x;
119             ll down = y;
120             int remains = y % x;
121             up += (ll)(a[need + 1] - a[need]) * remains;
122             gcd = GCD(up, down);
123             up /= gcd; down /= gcd;
124             printf("%lld/%lld\n", up, down);
125         }
126     }
127 }
128 
129 int main(){
130 
131     solve();
132 
133     return 0;
134 }

 

相關文章