网络流
来源:互联网 发布:php就业发展前景 编辑:程序博客网 时间:2024/06/17 00:38
网络流
一.最大流问题
1.增广路算法
1).Ford-Fulkerson算法: 残留网络中寻找增加路径
STEP0:置初始可行流。
STEP1:构造原网络的残量网络,在残量网络中找s-t有向路。如果没有,算法得到最大流结束。否则继续下一步。
STEP2:依据残量网络中的s-t有向路写出对应到原网络中的s-t增广路。对于增广路中的前向弧,置s(e)=u(e)- f(e)。对于反向弧,置s(e)=f(e)
STEP3:计算crement=min{s(e1),s(e2),…,s(ek)}
STEP4:对于增广路中的前向弧,令f(e)=f(e)+crement;对于其中的反向弧,令f(e)=f(e)-crement,转STEP1。
2).Edmonds-Korp算法
算法实现:
<span style="font-size:18px;"><strong>#include<stdio.h>#include<string.h>#include<queue>#define M 0x7fffffff#define Max 506using namespace std;int cap[Max][Max],f[Max][Max];int pre[Max]; //记录增广路径 int p[Max]; //记录增广时的残量int n,m;int EK(int s,int t){int v,u;int sum=0;queue<int >q;memset(f,0,sizeof(f));//memset(pre,0,sizeof(pre));while(true) //BFS找增广路{memset(p,0,sizeof(p));p[s]=M;q.push(s);while(!q.empty()){u=q.front();q.pop();for(v=1;v<=m;v++){if(!p[v] && f[u][v]<cap[u][v]) //找到新节点{pre[v]=u; //记录v的父亲,并加入FIFO队列q.push(v);p[v]=p[u]<cap[u][v]-f[u][v]?p[u]:cap[u][v]-f[u][v]; //s-v路径上的最小残量 }}}if(p[t]==0) break; //找不到增广路,则当前流已经是最大流(最小割最大流定理)for(u=t;u!=s;u=pre[u]){f[pre[u]][u]+=p[t]; //更新正向流量 f[u][pre[u]]-=p[t]; //更新反向流量 }sum+=p[t]; //更新从s流出的总流量 }return sum;}int main (){int i;int a,b,c;while (~scanf("%d%d",&n,&m)){memset(cap,0,sizeof(cap));for(i=0;i<n;i++){scanf("%d%d%d",&a,&b,&c);cap[a][b]+=c; //输入可能相同的边}printf("%d\n",EK(1,m)); }return 0;}</strong></span>
2.预流推进算法(Dinic算法)
步骤0:构造初始预流flow。
对源顶点s的每条出边(s,v),令flow(s,v)=cap(s,v);
对其余边(u,v),令flow(u,v)=0。构造一有效的高度函数h。
步骤1:如果残量网络中不存在活顶点,则计算结束,已经得到最大流.否则转步骤2。
步骤2:在网络中选取活顶点v。
如果存在顶点v的出边为可推流边,则选取一条这样的可推流边,并沿此边推流。
否则,令h(v) = min{h(w)+1 | (v,w)是当前残流网络中的边},并转步骤1。
算法实现:
<span style="font-size:18px;"><strong>#include <cstdio> #include <cstring> #include <queue> #define MAXN 205 #define INF 1000000000 using namespace std; struct Edge { int from, to, cap, flow; }; struct Dinic { int n, m, s, t; vector<Edge> edges; //边表.edges[e]和edges[e^1]互为反向弧 vector<int> G[MAXN]; //邻接表,G[i][j]表示结点i的第j条边在e数组中的序号 bool vis[MAXN]; //BFS使用 int d[MAXN]; //从起点到i的距离 int cur[MAXN]; //当前弧指针 void ClearAll(int n) { for (int i = 0; i < n; i++) G[i].clear(); edges.clear(); } void AddEdge(int from, int to, int cap) { edges.push_back((Edge) {from, to, cap, 0}); edges.push_back((Edge) {to, from, 0, 0}); m = edges.size(); G[from].push_back(m - 2); G[to].push_back(m - 1); } bool BFS() {//使用BFS计算出每一个点在残量网络中到t的最短距离d. memset(vis, 0, sizeof(vis)); queue<int> Q; Q.push(s); vis[s] = 1; d[s] = 0; while (!Q.empty()) { int x = Q.front(); Q.pop(); for (int i = 0; i < G[x].size(); i++) { Edge& e = edges[G[x][i]]; if (!vis[e.to] && e.cap > e.flow) { //只考虑残量网络中的弧 vis[e.to] = 1; d[e.to] = d[x] + 1; Q.push(e.to); } } } return vis[t]; } int DFS(int x, int a) {//使用DFS从S出发,沿着d值严格递减的顺序进行多路增广。 if (x == t || a == 0) return a; int flow = 0, f; for (int& i = cur[x]; i < G[x].size(); i++) { Edge& e = edges[G[x][i]]; if (d[x] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap - e.flow))) > 0) { e.flow += f; edges[G[x][i] ^ 1].flow -= f; flow += f; a -= f; if (a == 0) break; } } return flow; } int Maxflow(int s, int t) { this->s = s; this->t = t; int flow = 0; while (BFS()) { memset(cur, 0, sizeof(cur)); flow += DFS(s, INF); } return flow; } }; Dinic g; int main() { int n, m, i, a, b, c; while (~scanf("%d%d", &m, &n)) { g.ClearAll(n + 1); for (i = 0; i < m; i++) { scanf("%d%d%d", &a, &b, &c); g.AddEdge(a, b, c); } int flow = g.Maxflow(1, n); printf("%d\n", flow); } return 0; } </strong></span>
二.最小费用最大流
求解最小费用流的步骤和求最大流的步骤几乎完全一致,只是在步骤1时选一条非饱和路时,应选代价和最小 的路,即最短路。
步骤1. 选定一条总的单位费用最小的路,即要给定最小费用的初始可行流,而不是包含边数最小的路。
步骤2. 不断重复求最大流的步骤来进行,直到没有饱和路存在为止。然后计算每个路的总费用。
求解最小费用流的步骤和求最大流的步骤几乎完全一致,只是在步骤1时选一条非饱和路时,应选代价和最小 的路,即最短路。
步骤1. 选定一条总的单位费用最小的路,即要给定最小费用的初始可行流,而不是包含边数最小的路。
步骤2. 不断重复求最大流的步骤来进行,直到没有饱和路存在为止。然后计算每个路的总费用。
算法实现
#include <iostream> #include <queue> using namespace std; #define INF 1000000 const int MAXN=1000; /*********************************/ * 最小割最大流算法 * 可以在求得最大流的同时获取最小割集S,T * 邻接矩阵存储各类信息 /*********************************/ int cap[MAXN][MAXN]; //容量 ,没有边为0 int flow[MAXN][MAXN]; //流量 int cost[MAXN][MAXN]; //耗费矩阵 int p[MAXN]; //增广路前驱 int d[MAXN]; //s-t路径最小耗费 bool inq[MAXN]; //队列标记 int n; // 顶点数目 int f; //最大流 int s,t; //源点,汇点 int c; //最大流下最小耗费 /*********************************/ * 邻接矩阵读入图数据 * 例子: 4 0 2 1 0 0 0 1 1 0 0 0 1 0 0 0 0 0 2 5 0 -2 0 2 3 -5 -2 0 1 0 -3 -1 0 注明: 费用矩阵是对称的,有i,j的费用,则j,i费用为其相反数 /*********************************/ void read() { cin>>n; for(int i=0;i<n;++i) { for(int j=0;j<n;++j) { cin>>cap[i][j]; } } for(int i=0;i<n;++i) { for(int j=0;j<n;++j) { cin>>cost[i][j]; } } } /*********************************/ * void minFlow() * 最小费用最大流算法 * SPFA()算法找最小耗费增广路 * SPFA()其实是Bellman的一个小变形 * 求得的f一定是最大流,求得的c是最大流下最小耗费 /*********************************/ void minFlow() { cin>>s>>t; queue<int> q; memset(flow,0,sizeof(flow)); c=f=0; while(1) { memset(inq,0,sizeof(inq)); for(int i=0;i<n;++i) { d[i]= i==s ? 0:INF; } q.push(s); while(!q.empty()) { int u=q.front(); q.pop(); inq[u]=false; for(int v=0;v<n;v++) { if(cap[u][v]-flow[u][v]>0&&d[v]>d[u]+cost[u][v]) { d[v]=d[u]+cost[u][v]; p[v]=u; if(!inq[v]) { inq[v]=true; q.push(v); } } } } if(d[t]==INF) { cout<<"最大流:"<<f<<",最小耗费:"<<c<<endl; break; } int a=INF; for(int u=t;u!=s;u=p[u]) { if(cap[p[u]][u]-flow[p[u]][u]<a) { a=cap[p[u]][u]-flow[p[u]][u]; } } for(int u=t;u!=s;u=p[u]) { flow[p[u]][u]+=a; flow[u][p[u]]-=a; } c+=d[t]*a; f+=a; } } int main() { readG(); minCmaxF(); return 0; }
0 0
- 【网络流】网络扩容
- 网络流
- 网络流
- 网络流
- 网络流
- 网络流
- 网络流
- 网络流
- 【网络流】
- 【网络流】
- 网络流
- 网络流
- 网络流
- 网络流
- 网络流
- 网络流
- 网络流
- 网络流
- 测试颜色
- Mysql join语句的优化
- js为datagrid动态插入行和列
- 使用Winedt的几个小技巧(转载)
- java摄像头截图
- 网络流
- CoordinateMatrix To IndexedRowMatrix or To RowMatrix then SVD
- 科普文之iis写权限漏洞
- 一、pairs vs ipairs
- Ubuntu的命令及设置等
- android- Button点击与监听器处理
- System.Security.Cryptography.CryptographicException: 系统找不到指定的文件
- 平年闰年示意图
- 八爪鱼大数据应用技能培训课程开始报名啦!