网络流
来源:互联网 发布:linux 网卡配置文件 编辑:程序博客网 时间:2024/06/17 00:08
最大网络流
思路:
不断寻找增广路
#include<bits/stdc++.h>#define MAX 10000#define CanToNode Edges[CanTo[Top][i]]#define Min(a,b) (a)<(b)?(a):(b)using namespace std;struct EdgeType{ int To, Cap, Flow; //To能去的点,Cap容量,Flow流量 };struct NetworkType{ int N,M; int Layer[MAX+5]; //存层数 vector <EdgeType> Edges; //存下每条边,会发现,所有偶数下标都是存的正向边,奇数下标都是存的反向边 vector <int> CanTo[MAX+5]; //存每个点可以走到的边【真正的是存Edges里的下标,因此是采用的邻接表存储【其实应该就是前向星!不过这两个总感觉很神似orz…… //但是为什么要这样存不直接 vector <EdgeType> CanTo[MAX+5]; ? //因为这样好定位反向边,因为直接Edges n+1或n-1即是反向边,而直接领接表存不好确定谁是谁的反向边 /*举例 连边 1 2 10 则 Edges[0]={2,10}; CanTo[1][0]=0; Edges[1]={1,10} CanTo[2][0]=1; 所以1节点的第一条边便是存在Edges的下表为0当中的,调用的时候直接Edges[CanTo[n][i]] 并且找0的反向边是1, 1的反向边是0; 再连边2 3 10 Edges[2]={3,10}; CanTo[2][1]=2; Edges[3]={2,10} CanTo[3][0]=3; 也即 */ void AddEdge(int From, int To, int Cap) //连点 { Edges.push_back((EdgeType){To,Cap,0}); //正向边,容量为Cap Edges.push_back((EdgeType){From,0,0}); //反向边,容量为0【解释一波要 int T=Edges.size(); //这里为了存CanTo这个数组用 CanTo[From].push_back(T-2); //正向边,是T-2 CanTo[To].push_back(T-1); //反向边,是T-1 } void Test() //用来测试而已…… { for (int i=1; i<=N; i++) { printf("The Node is:%d CanTo Many:%d",i,CanTo[i].size()); for (int j=0; j<CanTo[i].size(); j++) printf("\n%d : To: %d, Flow: %d, Cap: %d, In Array<Edges>'s Top:%d",j,Edges[CanTo[i][j]].To,Edges[CanTo[i][j]].Flow,Edges[CanTo[i][j]].Cap,CanTo[i][j]); printf("\n\n"); } } bool Bfs(int S, int E) //用来分层 { queue <int> Line; memset(Layer,0,sizeof(Layer)); Layer[S]=1;//注意这里必须要赋成1而不能0,因为后面是用!Layer来判断的,所以赋0,起点的层数会被更改 Line.push(S); while (!Line.empty()) //一直增广直到不能增break { int Top=Line.front(); Line.pop(); for (int i=0; i<CanTo[Top].size(); i++) if (!Layer[CanToNode.To] && CanToNode.Flow<CanToNode.Cap) //如果这个节点没有走过并且流量小于容量则可以增广。【这里就可以解释为什么反向边时是减了,因为反向边定义Cap为0,所以通过减来证明可以回流 { Layer[CanToNode.To]=Layer[Top]+1; Line.push(CanToNode.To); } //这里用CanToNode代替了 Edges[CanTo[Top][i]], 简化代码 } return Layer[E];//这里如果LayerE找到层数,返回真,否则没找到层数即为0,返回0为假 } int Dfs(int Top, int MinF, int &E) { if (Top == E || MinF == 0) return MinF; //如果已经找到终点,或者所能增的流为0,则不用继续找,返回所能增得流 int Ans=0,Remain; for (int i=0; i<CanTo[Top].size(); i++) if (Layer[CanToNode.To] == Layer[Top] + 1&& (Remain = Dfs(CanToNode.To,Min(MinF,CanToNode.Cap-CanToNode.Flow),E)) > 0) { CanToNode.Flow+=Remain; Edges[CanTo[Top][i] ^ 1].Flow-=Remain; Ans+=Remain; MinF-=Remain; if (!MinF) break; } return Ans; } int Dinic(int S, int E) { int Ans=0; while (Bfs(S,E)) { Ans+=Dfs(S,INT_MAX,E); } return Ans; } int EK(int S, int E) //EK { queue <int> Line; int FlowM[MAX+5],Pre[MAX+5],PreEdge[MAX+5],Ans=0; //FlowM存所找到的增广路,Pre存所找到的上一个节点下标,而PreEdge则是存路 while (1) //一直增广直到不能增break { memset(FlowM,0,sizeof(FlowM)); FlowM[S]=INT_MAX; //先赋一个INF初值 Pre[S]=0; //标记回溯更改的终点,即之后回溯更改Edge时用的 Line.push(S); while (!Line.empty()) //Dfs { int Top=Line.front(); Line.pop(); for (int i=0; i<CanTo[Top].size(); i++) if (!FlowM[CanToNode.To] && CanToNode.Flow<CanToNode.Cap) //如果这个节点没有走过并且流量小于容量则可以增广。【这里就可以解释为什么反向边时是减了,因为反向边定义Cap为0,所以通过减来证明可以回流 { FlowM[CanToNode.To]+=Min(FlowM[Top],CanToNode.Cap-CanToNode.Flow); //存储这个节点所能增广的值,要与当前节点所能增广与所找到的节点所能增广的大小比较取小 Pre[CanToNode.To]=Top; //存节点 PreEdge[CanToNode.To]=CanTo[Top][i]; //存增光路的边 Line.push(CanToNode.To); } //这里用CanToNode代替了 Edges[CanTo[Top][i]], 简化代码 } if (!FlowM[E]) break; //如果没有找到到终点的路,增广完成 Ans+=FlowM[E]; //记录最大流 int Top=E; //从终点,借用Pre和PreEdge来更改值 while (Pre[Top]) { Edges[PreEdge[Top]].Flow+=FlowM[E]; //int AntiTop=PreEdge[Top]&1?PreEdge[Top]+1:PreEdge[Top]-1; //更改反边的下标,如果是%2==0则是正走的,==1则是反走的,存的是先正走再反走, //Edges[AntiTop].Flow-=FlowM[E]; Edges[PreEdge[Top] ^ 1].Flow-=FlowM[E]; //这里是个位运算基础操作[虽然我也不知道,异或1是用来取代反边的 ,上面注释掉的则是原来所写的方法 /*详细给自己讲一下 1^1=0;0^1=1; 即1异或1相当于-1,0异或1相当于+1 1^0=1;0^0=0; 即任何一个数异或0位本身 所以一个数n异或1; 则1(2)=000000001 n(2)=101001010[随便打的] 所以最后一位的前面的所有都是其本身,而最后一位则是相应的+1-1, 所以 0^1=1,1^1=0; 2^1=3;3^1=2; 4^1=5;5^1=4; 则完美匹配这种存储结构 */ Top=Pre[Top]; } } return Ans; }}Network;int main(){ int Start,End,SR,ER,FR; cin >> Network.N >> Network.M >> Start >> End; for (int i=1; i<=Network.M; i++) { cin >> SR >> ER >> FR; Network.AddEdge(SR,ER,FR); } //Network.Test(); //cout << Network.EK(Start,End) << endl; cout << Network.Dinic(Start,End);}
maye看着脑壳昏先把代码存……【第一次写工程代码orz……
回家再补qwq……
阅读全文
0 0
- 【网络流】网络扩容
- 网络流
- 网络流
- 网络流
- 网络流
- 网络流
- 网络流
- 网络流
- 【网络流】
- 【网络流】
- 网络流
- 网络流
- 网络流
- 网络流
- 网络流
- 网络流
- 网络流
- 网络流
- ubuntu 安装 Sqlite 和 可视化工具 SqliteBroswer
- Intellij Idea 13 如何 利用Maven 创建多模块 多Module web 项目
- Idea 自用快捷键
- java枚举类型总结篇
- Qt Creator和visual studio帮助文档形式上的区别
- 网络流
- 2017-11-18 记住这个日子
- Maven环境搭建和介绍
- (转)研究报告:区块链+供应链金融
- 指针数组、数组指针
- C语言实验——判断素数(循环结构)
- lib和dll文件的区别和联系
- opencv-python 绘制直方图和均衡化
- flask-wtf表单中PasswordField无法回传显示密码问题解决方法