2020牛客暑期多校训练营(第一场)H 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 }