• [算法] 网络中最小费用最大流


    前言

    如果还没有学过最大流,戳这里

    本文用到了优化的 (SPFA)简易的代码

    思路

    对于满足最大流合法的流函数可能有很多,但加上边的单位花费,则方案会减少很多。

    实质上是在 (Edmond-Karp) 算法上进行改进。

    (Edmond-Karp) 算法是对于残量网络上仅找到 (1) 条增广路进行增广。

    而求最小费用最大流只需要在寻找增广路的过程中寻找花费最小的增广路。

    建图

    需要包含四个信息:终点节点编号,边的容量,边的花费,相反的边的编号。

    struct Node {
    	int to, val, cost, rev;
    	Node() {}
    	Node(int T, int L, int C, int R) {
    		to = T;//终点节点编号
    		val = L;//边的容量
    		cost = C;//边的花费
    		rev = R;//相反的边的编号
    	}
    };
    

    确定增广路径

    (SPFA) 实现,若不存在增广路径,则停止增广。

    增广

    (SPFA) 的过程中记录这条最短路路径编号,并记录这条路上的最小剩余容量。

    用一个 (while) 循环寻找这条路径,并且直接记录这条路径的花费即可。

    C++代码

    #include <map>
    #include <queue>
    #include <cstdio>
    #include <vector>
    #include <cstring>
    using namespace std;
    #define INF 0x3f3f3f3f
    #define Min(a, b) ((a) < (b) ? (a) : (b))
    void Quick_Read(int &N) {
    	N = 0;
    	int op = 1;
    	char c = getchar();
    	while(c < '0' || c > '9') {
    		if(c == '-')
    			op = -1;
    		c = getchar();
    	}
    	while(c >= '0' && c <= '9') {
    		N = (N << 1) + (N << 3) + (c ^ 48);
    		c = getchar();
    	}
    	N *= op;
    }
    const int MAXN = 5e3 + 5;
    struct Node {
    	int to, val, cost, rev;
    	Node() {}
    	Node(int T, int L, int C, int R) {
    		to = T;
    		val = L;
    		cost = C;
    		rev = R;
    	}
    };
    vector<Node> v[MAXN];
    pair<int, int> pre[MAXN];
    deque<int> q;
    int dis[MAXN], maf[MAXN];
    bool inque[MAXN];
    int n, m, s, t;
    int ans;
    bool SPFA() {
    	for(int i = 1; i <= n; i++)
    		inque[i] = false, dis[i] = INF, maf[i] = -INF;
    	int iqn = 1, fis = 0;//优化
    	maf[s] = INF;
    	dis[s] = 0;
    	inque[s] = true;
    	q.push_back(s);
    	while(!q.empty()) {
    		int now = q.front(); q.pop_front();
    		inque[now] = false;
    		fis -= dis[now];
    		iqn--;
    		int SIZ= v[now].size();
    		for(int i = 0; i < SIZ; i++) {
    			int next = v[now][i].to;
    			if(dis[next] > dis[now] + v[now][i].cost && v[now][i].val) {
    				maf[next] = Min(maf[now], v[now][i].val);//记录最小剩余容量
    				pre[next].first = now;//记录路径	
    				pre[next].second = i;
    				dis[next] = dis[now] + v[now][i].cost;
    				if(!inque[next]) {
    					inque[next] = true;
    					if(q.empty() || dis[next] > dis[q.front()] || dis[next] * iqn <= fis)
    						q.push_back(next);
    					else
    						q.push_front(next);
    					fis += dis[next] + v[now][i].cost;
    					iqn++;
    				}
    			}
    		}
    	}
    	return dis[t] != INF;//是否存在增广路
    }
    int Update() {
    	int now = t;
    	while(now != s) {
    		int next = pre[now].first;
    		int i = pre[now].second;
    		v[next][i].val -= maf[t];//减去最小该路径的最小剩余容量,更新剩余流量
    		v[now][v[next][i].rev].val += maf[t];//流量守恒
    		ans += v[next][i].cost * maf[t];//记录价值
    		now = next;
    	}
    	return maf[t];
    }
    int Edmond_Karp() {//求解最小费用最大流
    	int res = 0;
    	while(SPFA()) {
    		res += Update();
    	}
    	return res;
    }
    void Read() {
    	int A, B, C, D;
    	Quick_Read(n);
    	Quick_Read(m);
    	Quick_Read(s);
    	Quick_Read(t);
    	for(int i = 1; i <= m; i++) {
    		Quick_Read(A);
    		Quick_Read(B);
    		Quick_Read(C);
    		Quick_Read(D);
    		int idA = v[A].size();
    		int idB = v[B].size();
    		v[A].push_back(Node(B, C, D, idB));
    		v[B].push_back(Node(A, 0, -D, idA));//记得双向存边
    	}
    }
    int main() {
    	Read();
    	printf("%d ", Edmond_Karp());
    	printf("%d", ans);
    	return 0;
    }
    

    看看自己做对没有吖

  • 相关阅读:
    C++初识
    实现小程序iphonex与iphone6底部适配
    vue-element实现外部独立全选
    flex中图片元素被挤压问题
    vue+element-Ui实现简单的表单必填项验证(1)
    Vuex——项目应用(1)
    Vue项目小应用
    transtion过渡
    axios的高阶用法
    阿里云服务器初步使用介绍
  • 原文地址:https://www.cnblogs.com/C202202chenkelin/p/14159053.html
Copyright © 2020-2023  润新知