2020牛客暑期多校训练营(第一场)H Minimum-cost Flow

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 }

 

posted @ 2020-07-14 18:48  SummerMingQAQ  阅读(297)  评论(0编辑  收藏  举报