费用流(板子)

  • 最小费用最大流
  • 最大费用最小流,建边的时候,把花费和流量调换位置
  • 最大费用最大流,建边的时候,把正向的费用存为负值,结果取负号

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; // 返回最大流
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值