題目:給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 }