【題意】:有n個城市,m條有方向的路。你想從城市1運(yùn)輸k個單位的貨物到城市n,但每條路上都有很多強(qiáng)盜,因此通過每條路你都必須要小心的保護(hù)好你的貨物。對于每條路i,給出一個系數(shù)ai,如果你想在這條路上運(yùn)輸x個單位的貨物,就要花費(fèi)ai*x*x的費(fèi)用以便去保護(hù)你的貨物;再給出一個ci,表示在這條路上最多運(yùn)輸ci個單位的貨物。如果能運(yùn)輸完所有貨物的話,輸出最小的費(fèi)用;否則,輸出-1。

【題解】:這題好明顯是一個最小費(fèi)用流的模型。但問題在于,通過每條路的費(fèi)用不是跟流成正比,而是跟流的平方成正比。
              觀察數(shù)據(jù),發(fā)現(xiàn)每條路的容量ci <= 5,是不是覺得好奇怪,為什么邊的容量ci <= 5。其實,題目這是在暗示我們要拆邊。
              舉一個例子,有邊<u,v>,c為5,a為1。
              我們在<u,v>上分別運(yùn)輸x個單位貨物:
                            x                  1      2      3      4      5
                          cost                1      4      9     16     25
                 cost[x] - cost[x-1]            3      5      7      9        觀察這一行,顯然這是一個等差數(shù)列。   n^2 = 1 + 3 + 5 + … +(2n - 1). [關(guān)鍵:拆邊的依據(jù)]
              觀察到這個規(guī)律:我們就可以對所有邊<u,v> ,ai,ci這樣處理,把當(dāng)前邊拆分成 ci 條<u,v>邊,每條邊的費(fèi)用是 (2*i - 1)* ai  [ 1 <= i <= ci ],容量都是1。為了下面方便,我們叫這樣的ci條邊為一組邊。
              拆完之后,圖上每條邊的容量都變成1,而且邊上的費(fèi)用跟流成正比。
              對于每組邊的選擇,我們肯定是從編號小的先選起,因為編號小的費(fèi)用小。假如對于某組邊,我們選擇了其中x條,那就意味著我們在原<u,v>邊上運(yùn)輸了x個單位的貨物,那么費(fèi)用應(yīng)該是ai*x*x。而前x條邊的費(fèi)用總和 = [ 1 + 3 + … + (2 * x - 1)] * ai = x*x*ai。這就證明了我們拆邊的正確性。
              又因為題目問最終能否運(yùn)輸完k個單位的貨物,對于這個問題我們有兩種處理辦法:
              1.加入一個超級源點(diǎn)s,連邊s->1,容量為k,費(fèi)用為0.表示最多運(yùn)k個單位貨物。
              2.限制費(fèi)用流運(yùn)行的條件,平時費(fèi)用流限制條件是能否找到增廣路,我們只需再加入一個條件,ans < k(ans表示當(dāng)前的費(fèi)用流的流量)。
              最后判斷一下ans是否等于k就可以了。

【代碼】:
 1 #include "iostream"
 2 #include "cstring"
 3 #include "cstdio"
 4 #include "queue"
 5 using namespace std;
 6 #define maxn 105
 7 #define maxm 100005
 8 const int inf = 1 << 30;
 9 int n, m, k, s, t;
10 int eh[maxn], low[maxn], tot, dist[maxn], pre[maxn], ans, anscost;
11 bool visit[maxn];
12 
13 struct Edge {
14     int u, v, cost, cap, flow, next;
15 }et[maxm];
16 
17 void init() {
18     tot = 0;
19     memset(eh, -1sizeof(eh));
20 }
21 
22 void add(int u, int v, int cost, int cap, int flow) {
23     Edge e = {u, v, cost, cap, flow, eh[u]};
24     et[tot] = e;
25     eh[u] = tot++;
26 }
27 
28 void addedge(int u, int v, int cost, int cap) {
29     add(u, v, cost, cap, 0), add(v, u, -cost, 00);
30 }
31 
32 bool spfa() {
33     fill(&dist[0], &dist[maxn], inf);
34     memset(visit, falsesizeof(visit));
35     memset(pre, -1sizeof(pre));
36     low[s] = inf, visit[s] = true, dist[s] = 0;
37     queue<int> que;
38     que.push(s);
39     while(!que.empty()) {
40         int u = que.front();
41         que.pop();
42         visit[u] = false;
43         for(int i = eh[u]; i != -1; i = et[i].next) {
44             int v = et[i].v, cost = et[i].cost, cap = et[i].cap, flow = et[i].flow;
45             if(cap - flow && dist[v] > dist[u] + cost) {
46                 dist[v] = dist[u] + cost;
47                 pre[v] = i;
48                 low[v] = min(low[u], cap - flow);
49                 if(!visit[v]) {
50                     que.push(v);
51                     visit[v] = true;
52                 }
53             }
54         }
55     }
56     return dist[t] != inf;
57 }
58 
59 void costflow() {
60     ans = anscost = 0;
61     while(ans < k && spfa()) {
62         int x = pre[t];
63         ans += low[t];
64         anscost += low[t] * dist[t];
65         while(x != -1) {
66             et[x].flow += low[t];
67             et[x^1].flow -= low[t];
68             x = pre[et[x].u];
69         }
70     }
71 }
72 
73 int main() {
74     int u, v, cost, cap;
75     while(~scanf("%d%d%d"&n, &m, &k)) {
76         init();
77         s = 1, t = n;
78         for(int i = 0; i < m; i++) {
79             scanf("%d%d%d%d"&u, &v, &cost, &cap);
80             for(int j = 1; j <= cap; j++)
81                 addedge(u, v, (2 * j - 1* cost, 1);
82         }
83         costflow();
84         if(ans == k) printf("%d\n", anscost);
85         else printf("%d\n"-1);
86     }
87     return 0;
88 }