- 最小费用流就是在保证流量
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: zhengdongjian@tju.edu.cn
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: zhengdongjian@tju.edu.cn
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: zhengdongjian@tju.edu.cn
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: zhengdongjian@tju.edu.cn
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: zhengdongjian@tju.edu.cn
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;
}