- 最小费用最大流
- 最大费用最小流,建边的时候,把花费和流量调换位置
- 最大费用最大流,建边的时候,把正向的费用存为负值,结果取负号
Spfa:链式向前星
const int maxn = ;
const int inf = 0x3f3f3f3f;
//path用来保存找到一条费用最小的增广路
int path[maxn], dis[maxn], head[maxn], vis[maxn], cnt;
void init() {
cnt = 0;
memset(head, -1, sizeof(head));
}
struct ac {
int v, flow, cost, nex;
}edge[maxn];
void addEdge(int u, int v, int flow, int cost) {
edge[cnt] = {v, flow, cost, head[u]};
head[u] = cnt++;
edge[cnt] = {u, 0, -cost, head[v]};
head[v] = cnt++;
}
int Spfa(int s, int t) {
memset(dis, inf, sizeof(dis));
memset(vis, 0, sizeof(vis));
memset(path, -1, sizeof(path));
queue<int> que;
que.push(s);
dis[s] = 0;
vis[s] = 1;
while(!que.empty()) {
int u = que.front();
que.pop();
vis[u] = 0;
for (int i = head[u]; i != -1; i = edge[i].nex) {
int v = edge[i].v;
int flow = edge[i].flow;
int cost = edge[i].cost;
if (dis[v] > dis[u] + cost && flow > 0) {
dis[v] = dis[u] + cost;
path[v] = i;
if(vis[v]) continue;
vis[v] = 1;
que.push(v);
}
}
}
return dis[t] != inf;
}
int MCMF(int s, int t, int &cost) { //最少费用最大流
int maxflow = 0;
while(Spfa(s, t)) { //Spfa 找最小花费最大流
int flow = inf;
//遍历该路上的边,找到最小流量
//path 存的是这条路的路径,
//edge[i^1].v 通过反向边得到前驱节点
for(int i = path[t]; i != -1; i = path[edge[i ^ 1].v]){
flow = min(flow, edge[i].flow);
}
//找到最小流量后,更新该路上的边的流量
for(int i = path[t]; i != -1; i = path[edge[i ^ 1].v]) {
edge[i].flow -= flow;
edge[i ^ 1].flow += flow;
cost += flow * edge[i].cost;
}
maxflow += flow;
}
return maxflow; //最大流
}
类Dinic:链式向前
时间复杂度 :可以证明上界为 O( n * m *f ) ,其中 f 表示流量。
const int maxn = ;
const int maxm = ;
const int inf = 0x3f3f3f3f;
int cur[maxn], head[maxn], dis[maxn], cnt;
bool vis[maxn];
int MinCost = 0;
struct ac {
int v, cap, cost, nxt;
}edge[maxm];
int init() {
cnt = 1;
MinCost = 0;
memset(head, 0, sizeof(head));
}
void addEdge(int u, int v, int cap, int cost) {
edge[++cnt] = {v, cap, cost, head[u]};
head[u] = cnt;
edge[++cnt] = {u, 0, -cost, head[v]};
head[v] = cnt;
}
bool Spfa (int s, int t) { //找到费用最小从s 到 t的一条费用最小的曾广路
memset(dis, inf, sizeof(dis)); // 记录从s点出发到每个点的费用和最小值
memcpy(cur, head, sizeof(cur)); //用来多路曾广的弧优化
std::queue<int> que;
que.push(s);
dis[s] = 0, vis[s] = 1;
while (!que.empty()) {
int u = que.front();
que.pop();
vis[u] = 0;
//遍历u的所有出边
for(int i = head[u]; i; i = edge[i].nxt) {
int v = edge[i].v;
int cost = edge[i].cost;
int cap = edge[i].cap;
if(cap > 0 && dis[v] > dis[u] + cost) {
dis[v] = dis[u] + cost;
if(vis[v]) continue; //如果已经在队内
que.push(v);
vis[v] = 1;
}
}
}
return dis[t] != inf;
}
int Dfs(int u, int t, int flow) {
if(u == t) return flow;
vis[u] = 1;
int Ans = 0;
for(int &i = cur[u]; i && Ans < flow; i = edge[i].nxt) {
int v = edge[i].v;
int cap = edge[i].cap;
int cost = edge[i].cost;
if(!vis[v] && cap && dis[v] == dis[u] + cost) {
int minflow = Dfs(v, t, std::min(cap, flow - Ans));
if(minflow) {
MinCost += minflow * cost;
edge[i].cap -= minflow;
edge[i^1].cap += minflow;
Ans += minflow;
}
}
}
vis[u] = 0;
return Ans;
}
int MCMF(int s, int t) {
int Ans = 0;
while(Spfa(s, t)) {
int x;
while((x = Dfs(s, t, inf))) Ans += x;
}
return Ans;
}
Dijkstra + 链式:
const int maxn = 500;
const int inf = 0x3f3f3f3f;
int preE[maxn], preV[maxn], dis[maxn], head[maxn], h[maxn], cnt;
bool vis[maxn];
void init() {
cnt = 0;
memset(head, -1, sizeof(head));
}
struct ac{
int v, cap, cost, nxt;
}edge[maxn << 8];
void addEdge(int u, int v, int cap, int cost) {
edge[cnt] = {v, cap, cost, head[u]};
head[u] = cnt++;
edge[cnt] = {u, 0, -cost, head[v]};
head[v] = cnt++;
}
int Dijkstra(int s, int t) {
memset(dis, inf, sizeof(dis));// 记录从s点出发到每个点的费用和最小值
preE[s] = -1, dis[s] = 0;
//按花费的升序排序
priority_queue< pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > que;
que.push(pair<int, int>(0, s));
while (!que.empty()) {
//找花费最小的点
pair<int, int> top = que.top();
que.pop();
int u = top.second;
if (dis[u] < top.first) continue; //如果花费无法更小就退出
for(int i = head[u]; i != -1; i = edge[i].nxt) {
int v = edge[i].v;
int cap = edge[i].cap;
int cost = edge[i].cost;
//判断能不能继续增广
if (dis[v] > dis[u] + cost + h[u] - h[v] && cap > 0) {
dis[v] = dis[u] + cost + h[u] - h[v];
preE[v] = i; //记录从哪条边来的
preV[v] = u; //记录从哪个点来的
que.push(pair<int, int>(dis[v], v));
}
}
}
return dis[t] != inf;
}
int MCMF(int s, int t, int &cost) {
int maxflow = 0;
memset(h, 0, sizeof(h));
while(Dijkstra(s, t)) { //Dijkstra 找看是否存在增广路,如果存在,求出费用和最小的一条
for(int i = 0; i <= t; ++i) h[i] += dis[i];
int flow = inf;
//
for (int i = t; i != s; i = preV[i]){
flow = min(flow, edge[preE[i]].cap); //取最小流量
}
for (int i = t; i != s; i = preV[i]) {
edge[preE[i]].cap -= flow;
edge[preE[i] ^ 1].cap += flow;
}
cost += flow * h[t]; //每个单位流量都有费用
maxflow += flow;
}
return maxflow;
}
Djikstra + vector:
const int maxn = ;
const int maxm = ;
const int inf = 0x3f3f3f3f;
int preV[maxn], preE[maxn]; // 前驱节点,和对应边
int dis[maxn], h[maxn];
struct ac {
int v, cap, cost, rev; //rev记录反向边
};
vector<ac> G[maxn << 3];
void init() {
for(int i = 0; i < maxn; ++i) G[i].clear();
}
void addedge(int u, int v, int cap, int cost) {
G[u].push_back({v, cap, cost, (int)G[v].size()});
G[v].push_back({u, 0, -cost, (int)G[u].size() - 1});//-1是因为u刚刚+一条边
}
int Dijkstra(int s, int t) {
priority_queue< pair<int,int>, vector<pair<int,int> >, greater<pair<int,int> > >que;
que.push(pair<int,int>(0, s));
memset(dis, inf, sizeof(dis));
dis[s] = 0;
while (!que.empty()) {
pair<int, int> top = que.top();
que.pop();
int u = top.second;
if (dis[u] < top.first) continue;
for (int i = 0; i < (int)G[u].size(); ++i) {
int v = G[u][i].v;
int cost = G[u][i].cost;
int cap = G[u][i].cap;
if (cap > 0 && dis[v] > dis[u] + cost + h[u] - h[v]) {
dis[v] = dis[u] + cost + h[u] - h[v];
preE[v] = i;
preV[v] = u;
que.push(pair<int,int>(dis[v], v));
}
}
}
return dis[t] != inf;
}
int MCMF(int s, int t, int &cost) {
int maxflow = 0;
memset(h, 0, sizeof(h));
while (Dijkstra(s, t)) { // 搜先spfa看是否存在增广路,如果存在求一条费用和最小的一条
for (int i = 1; i <= t; ++i) h[i] += dis[i];
int flow = inf;
for (int i = t; i != s; i = preV[i]) {
flow = min(flow, G[preV[i]][preE[i]].cap); // 取最小的流量
}
for (int i = t; i != s; i = preV[i]) {
ac &e = G[preV[i]][preE[i]];
e.cap -= flow;
G[i][e.rev].cap += flow;
}
cost += flow * h[t];
maxflow += flow;
//cout << cost << endl;
}
return maxflow; // 返回最大流
}