- 最小費用流就是在保證流量
F 的前提下,求解最少花費是多少的問題O_O - 建圖: 每條邊在最大流基礎上
(to指向點,cap容量,rev反向邊) ,增加一個費用(cost) - 求解方法: 在最大流中,是按照任意可行路徑,也就是殘餘網絡中的可增廣路徑,向網絡中注入流量,從而在匯點獲得相應流量的策略進行,直到不存在可增廣路。簡單分析後可以知道:
- 如果原圖的最大流
F′<F ,則顯然不存在所謂最小費用最大流 - 將邊的費用看做邊連接兩點的距離,那麼如果存在兩條流量相同的可增廣路徑
S1 和S2 (flow1=flow2) ,並且二者的路徑長度(也即路徑上所有邊的費用和)滿足那麼顯然前者是更優的選擇。反之亦然,即費用相同時流量越大越好。於是可貪心選取。(Distance1<Distance2) - 爲什麼不用擔心上述貪心會導致: 由於選擇了更大流量的路徑而影響後面的貪心錯誤呢?原因就和基於增廣路算法的最大流一樣啦,窩們不是加了反向邊麼
t_t
- 如果原圖的最大流
- 基於上述求解方法的思路,窩們只需要每次從源點
Source 向匯點Destination 選擇最短路然後增廣其可容納的最大流量即可。 - 複雜度相關: 如果採用
Bellman ˙Ford 求最短路,每次獲得的最少流量爲1 ,於是複雜度爲O(F∗|V|∗|E|) ,堆優化的Dijstra 可以做到O(F∗|E|∗log|V|) 。但Bellman 可以處理負權邊,也就是可以處理費用爲負值的圖 - 似乎理解了上述,便足夠了,不過還是放兩份分別用
Dijstra 和Bellman 實現的模板,前者還加入了一個有用的頂點勢標記來處理負權環. - 模板1
/* **********************************************
File Name: min_cost_flow.cpp
Auther: [email protected]
Created Time: 2015年08月05日 星期三 13時06分05秒
*********************************************** */
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
const double EPS = 1e-8;
const double PI = acos(-1.0);
typedef pair<int, int> P;
const int INF = 0xfffffff;
const int MAX = 10007;
struct Edge
{
int to;
int cap;
int cost;
int rev;
};
int V; //[0, V)
vector<Edge> G[MAX];
int h[MAX]; //頂點的勢
int dist[MAX]; //min_distance
int prevv[MAX]; //前驅
int preve[MAX]; //前驅對應邊
inline void add_edge(int from, int to, int cap, int cost)
{
G[from].push_back((struct Edge){to, cap, cost, (int)G[to].size()});
G[to].push_back((struct Edge){from, 0, -cost, (int)G[from].size() - 1});
}
int min_cost_flow(int s, int t, int f)
{
int res = 0;
memset(h, 0, sizeof(h));
while (f > 0)
{
priority_queue<P, vector<P>, greater<P> > Q;
fill(dist, dist + V, INF);
dist[s] = 0;
Q.push(P(dist[s], s));
while (!Q.empty())
{
P p = Q.top();
Q.pop();
int v = p.second;
if (dist[v] < p.first) continue;
for (int i = 0; i < (int)G[v].size(); ++i)
{
Edge& e = G[v][i];
if (e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to])
{
dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];
prevv[e.to] = v;
preve[e.to] = i;
Q.push(P(dist[e.to], e.to));
}
}
}
if (dist[t] == INF)
{
return -1; //no more route to go
}
for (int v = 0; v < V; ++v)
{
h[v] += dist[v];
}
int d = f;
for (int v = t; v != s; v = prevv[v])
{
d = min(d, G[prevv[v]][preve[v]].cap);
}
f -= d;
res += d * h[t];
for (int v = t; v != s; v = prevv[v])
{
Edge& e = G[prevv[v]][preve[v]];
e.cap -= d;
G[v][e.rev].cap += d;
}
}
return res;
}
int main()
{
return 0;
}
- 模板2
/* **********************************************
File Name: min_cost_flow_bellman.cpp
Auther: [email protected]
Created Time: 2015年08月05日 星期三 14時29分57秒
*********************************************** */
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
const double EPS = 1e-8;
const double PI = acos(-1.0);
typedef pair<int, int> P;
const int INF = 0xfffffff;
const int MAX = 10007;
struct Edge
{
int to;
int cap;
int cost;
int rev;
};
int V; //[0, V)
vector<Edge> G[MAX];
int dist[MAX]; //min_distance
int prevv[MAX]; //前驅
int preve[MAX]; //前驅對應邊
inline void add_edge(int from, int to, int cap, int cost)
{
G[from].push_back((struct Edge){to, cap, cost, (int)G[to].size()});
G[to].push_back((struct Edge){from, 0, -cost, (int)G[from].size() - 1});
}
int min_cost_flow(int s, int t, int f)
{
int res = 0;
while (f > 0)
{
fill(dist, dist + V, INF);
dist[s] = 0;
bool update = true;
while (update)
{
update = false;
for (int v = 0; v < V; ++v)
{
if (dist[v] == INF) continue;
for (int i = 0; i < (int)G[v].size(); ++i)
{
Edge& e = G[v][i];
if (e.cap > 0 && dist[e.to] > dist[v] + e.cost)
{
dist[e.to] = dist[v] + e.cost;
prevv[e.to] = V;
preve[e.to] = i;
update = true;
}
}
}
}
if (dist[t] == INF)
{
return -1; //no more route to go
}
for (int v = 0; v < V; ++v)
{
h[v] += dist[v];
}
int d = f;
for (int v = t; v != s; v = prevv[v])
{
d = min(d, G[prevv[v]][preve[v]].cap);
}
f -= d;
res += d * dist[t];
for (int v = t; v != s; v = prevv[v])
{
Edge& e = G[prevv[v]][preve[v]];
e.cap -= d;
G[v][e.rev].cap += d;
}
}
return res;
}
int main()
{
return 0;
}
簡單補充:最大權閉合圖
- 建圖:每個點向其必要條件連邊,容量
INF ,增加源點和匯點,源點向正權點連邊,容量權值,負權點向匯點連邊,容量權值的絕對值,用該圖跑最大流最大權=∑wi≥0wi−maxflow
補充2:dinic網絡流鄰接表實現
- 連續若干次用
vector 啥的搞粗來的dinic 紛紛跪掉,於是重新整理了一份鄰接表版本
/* **********************************************
File Name: dinic_list.cpp
Auther: [email protected]
Created Time: 2015年08月17日 星期一 15時48分47秒
*********************************************** */
#include <bits/stdc++.h>
using namespace std;
const int INF = 0xfffffff;
//最大頂點數
//max vertix
const int MAX_N = 1024;
//最大邊數.注意: *必須*大於兩倍實際的邊數
//max edges.Warning: it should be TWO TIMES BIGGER than the edges truely exist.
const int MAX_E = 100007;
//隊列容量,儘量開大一點兒,實在開不下,用循環隊列
//max nodes in Queue. Warning: It should be BIG ENOUGH
const int MAX_Q = 1000007;
//邊
struct Edge {
int to;
int cap; //capcity, 容量
int nxt; //pointer, 鏈表的next指針
} G[MAX_E];
static int tot; //total edges. 記錄當前存儲邊數[0, tot)
int head[MAX_N]; //head pointer. 頭指針
int level[MAX_N]; //分層
int iter[MAX_N]; //當前弧
int Q[MAX_Q]; //queue, 隊列
inline void init() {
tot = 0;
memset(head, -1, sizeof(head));
}
//要保證每一對<邊,反向邊>可以互相異或得到
//In case each pair of edges should be with index <2k,2k+1> for some integer k
inline void add_edge(int u, int v, int c) {
G[tot].to = v, G[tot].cap = c;
G[tot].nxt = head[u];
head[u] = tot++;
G[tot].to = u, G[tot].cap = 0;
G[tot].nxt = head[v];
head[v] = tot++;
}
//返回: 是否還能增廣
//return value: whether there is any roads to gain more flows
bool bfs(int s, int t) {
int front = 0, rear = 0;
memset(level, -1, sizeof(level));
level[s] = 0;
Q[rear++] = s;
while (front ^ rear) {
int p = Q[front++];
for (int i = head[p]; ~i; i = G[i].nxt) {
Edge& e = G[i];
if (level[e.to] == -1 && e.cap > 0) {
level[e.to] = level[p] + 1;
Q[rear++] = e.to;
if (e.to == t) {
return true;
}
}
}
}
return false;
}
int dfs(int s, int t, int flow) {
if (s == t) return flow;
int sum = 0, tp;
for (int& i = iter[s]; ~i; i = G[i].nxt) {
Edge& e = G[i];
if (e.cap > 0 && level[e.to] == level[s] + 1) {
tp = dfs(e.to, t, min(flow, e.cap));
flow -= tp;
e.cap -= tp;
G[i ^ 1].cap += tp;
sum += tp;
if (flow == 0) {
break;
}
}
}
return sum;
}
int dinic(int s, int t) {
int sum = 0, tp;
while (bfs(s, t)) {
memcpy(iter, head, sizeof(head));
while (tp = dfs(s, t, INF)) {
sum += tp;
}
}
return sum;
}
int main() {
return 0;
}
順便過掉了兩個被卡若干次的題:
- hdu4888
/* **********************************************
File Name: 4888.cpp
Auther: [email protected]
Created Time: 2015/8/17 星期一 下午 12:47:01
*********************************************** */
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> P;
const int INF = INT_MAX >> 2;
const int MAX = 1024;
const int MAXE = 400007;
const int MAXQ = 1000007;
struct Edge {
//int from;
int to;
int cap;
int nxt;
} G[MAXE];
struct Edge2 {
int to;
int nxt;
} G2[MAXE];
int head[MAX];
int head2[MAX];
bool vis[MAX];
static int tot;
static int tot2;
int Q[MAXQ]; //queue
int level[MAX];
int iter[MAX];
inline void init() {
tot = 0;
memset(head, -1, sizeof(head));
}
inline void add_edge(int u, int v, int c) {
G[tot].to = v;
G[tot].cap = c, G[tot].nxt = head[u];
head[u] = tot++;
G[tot].to = u;
G[tot].cap = 0, G[tot].nxt = head[v];
head[v] = tot++;
}
inline void add_edge2(int u, int v) {
G2[tot2].to = v;
G2[tot2].nxt = head2[u];
head2[u] = tot2++;
}
bool bfs(int s, int t) {
memset(level, -1, sizeof(level));
level[s] = 0;
int front = 0, rear = 1;
Q[front] = s;
while (front != rear) {
int p = Q[front++];
for (auto i = head[p]; ~i; i = G[i].nxt) {
Edge& e = G[i];
if (level[e.to] == -1 && e.cap > 0) {
level[e.to] = level[p] + 1;
Q[rear++] = e.to;
if (e.to == t) {
return true;
}
}
}
}
return false;
}
int dfs(int s, int t, int flow) {
if (s == t) {
return flow;
}
int sum = 0, tp;
for (int& i = iter[s]; ~i; i = G[i].nxt) {
Edge& e = G[i];
if (level[e.to] == level[s] + 1 && e.cap > 0) {
tp = dfs(e.to, t, min(flow, e.cap));
sum += tp;
e.cap -= tp;
flow -= tp;
G[i ^ 1].cap += tp;
if (flow == 0) {
break;
}
}
}
return sum;
}
bool dfs2(int s, int fa) {
for (int i = head2[s]; ~i; i = G2[i].nxt) {
Edge2& e = G2[i];
if (e.to == fa) continue;
if (vis[e.to]) {
return true;
}
vis[e.to] = true;
if (dfs2(e.to, s)) {
return true;
}
vis[e.to] = false;
}
return false;
}
int dinic(int s, int t) {
int sum = 0, tmp;
while (bfs(s, t)) {
memcpy(iter, head, sizeof(head));
while (tmp = dfs(s, t, INF)) {
sum += tmp;
}
}
return sum;
}
inline int read() {
char c;
while ((c = getchar()) < '0' || c > '9');
int x = c - '0';
while ((c = getchar()) >= '0' && c <= '9') {
x = (x << 3) + (x << 1) + c - '0';
}
return x;
}
void rebuild(int s, int t) {
memset(head2, -1, sizeof(head2));
tot2 = 0;
for (int i = s + 1; i < t; ++i) {
for (int j = head[i]; ~j; j = G[j].nxt) {
if (G[j].cap > 0 && G[j].to > s && G[j].to < t) {
add_edge2(i, G[j].to);
//printf("add_edge <%d, %d>\n", i, G[j].to);
}
}
}
}
int main() {
int n, m, k;
while (~scanf(" %d %d %d", &n, &m, &k)) {
int s = 0, t = n + m + 1, x;
tot = 0;
memset(head, -1, sizeof(head));
int sum1 = 0, sum2 = 0;
for (int i = 1; i <= n; ++i) {
x = read();
sum1 += x;
add_edge(s, i, x);
}
for (int i = 1; i <= m; ++i) {
x = read();
sum2 += x;
add_edge(n + i, t, x);
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
add_edge(i, j + n, k);
}
}
if (sum1 != sum2) {
puts("Impossible");
continue;
}
int max_flow = dinic(s, t);
//continue;
//int max_flow = sum1;
if (max_flow != sum1) {
puts("Impossible");
} else {
bool flag = false;
rebuild(s, t);
memset(vis, false, sizeof(vis));
for (int i = 1; i <= n; ++i) {
//printf("gao %d\n", i);
//vis[i] = true;
if (dfs2(i, -1)) {
flag = true;
break;
}
vis[i] = false;
}
if (flag) {
puts("Not Unique");
} else {
puts("Unique");
int ans[MAX];
for (int i = 1; i <= n; ++i) {
memset(ans, 0, sizeof(ans));
for (int j = head[i]; ~j; j = G[j].nxt) {
ans[G[j].to] += k - G[j].cap;
}
printf("%d", ans[n + 1]);
for (int j = 2; j <= m; ++j) {
printf(" %d", ans[n + j]);
}
puts("");
}
}
}
}
return 0;
}
- ural1421
/* **********************************************
File Name: 1421.cpp
Auther: [email protected]
Created Time: 2015年08月17日 星期一 16時11分44秒
*********************************************** */
#include <bits/stdc++.h>
using namespace std;
const int INF = 0xfffffff;
//最大頂點數
//max vertix
const int MAX_N = 256;
//最大邊數.注意: *必須*大於兩倍實際的邊數
//max edges.Warning: it should be TWO TIMES BIGGER than the edges truely exist.
const int MAX_E = 30007;
//隊列容量,儘量開大一點兒,實在開不下,用循環隊列
//max nodes in Queue. Warning: It should be BIG ENOUGH
const int MAX_Q = 1000007;
//邊
struct Edge {
int to;
int cap; //capcity, 容量
int nxt; //pointer, 鏈表的next指針
} G[MAX_E];
static int tot;
int head[MAX_N]; //head pointer. 頭指針
int level[MAX_N]; //分層
int iter[MAX_N]; //當前弧
int Q[MAX_Q]; //queue, 隊列
inline void init() {
memset(head, -1, sizeof(head));
tot = 0;
}
inline void add_edge(int u, int v, int c) {
//printf("add edge %d, %d, %d\n", u, v, c);
G[tot].to = v;
G[tot].cap = c;
G[tot].nxt = head[u];
head[u] = tot++;
G[tot].to = u;
G[tot].cap = 0;
G[tot].nxt = head[v];
head[v] = tot++;
}
//返回: 是否還能增廣
//return value: whether there is any roads to gain more flows
bool bfs(int s, int t) {
int front = 0, rear = 0;
memset(level, -1, sizeof(level));
level[s] = 0;
Q[rear++] = s;
while (front ^ rear) {
int p = Q[front++];
//printf("explore %d\n", p);
for (int i = head[p]; ~i; i = G[i].nxt) {
Edge& e = G[i];
if (level[e.to] == -1 && e.cap > 0) {
level[e.to] = level[p] + 1;
Q[rear++] = e.to;
if (e.to == t) {
return true;
}
}
}
}
return false;
}
int dfs(int s, int t, int flow) {
if (s == t) return flow;
int sum = 0, tp;
for (int& i = iter[s]; ~i; i = G[i].nxt) {
Edge& e = G[i];
if (e.cap > 0 && level[e.to] == level[s] + 1) {
tp = dfs(e.to, t, min(flow, e.cap));
flow -= tp;
e.cap -= tp;
G[i ^ 1].cap += tp;
sum += tp;
if (flow == 0) {
break;
}
}
}
return sum;
}
int dinic(int s, int t) {
int sum = 0, tp;
while (bfs(s, t)) {
memcpy(iter, head, sizeof(head));
while (tp = dfs(s, t, INF)) {
sum += tp;
}
}
return sum;
}
int main() {
int n;
while (~scanf(" %d", &n)) {
init();
int s = 0, t = n + n + 1, x;
int sum = 0;
for (int i = 1; i <= n; ++i) {
scanf(" %d", &x);
sum += x;
add_edge(s, i, x);
}
for (int i = 1; i <= n; ++i) {
scanf(" %d", &x);
add_edge(i + n, t, x);
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
add_edge(i, j + n, 100);
}
}
int maxflow = dinic(s, t);
if (maxflow != sum) {
puts("NO");
} else {
puts("YES");
int flow[MAX_N];
for (int i = 1; i <= n; ++i) {
memset(flow, 0, sizeof(flow));
for (int j = head[i]; ~j; j = G[j].nxt) {
if (G[j].to > n) {
flow[G[j].to - n] += 100 - G[j].cap;
}
}
for (int j = 1; j <= n; ++j) {
printf("%d%c", flow[j], j == n ? '\n' : ' ');
}
}
}
}
return 0;
}