最大网络流
思路:
不断寻找增广路
#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……