今天接触了最大流,网上有很多ppt,耐心看下,再敲几遍代码大概就能懂意思了
EK 算法
关键是要理解要理解反悔的这个意思,因为每次当你选择了一种方式,但是这种方式不一定是最优的所以我们要再来建立一条反向边,
来完成反悔的策略
然后就是大概一直找增广路,改变最大的值,一直到找不到增广路为止
现在把模板的代码附上,并且给予注释
下面有两种方式一种是紫书上刘汝佳的代码,还有种是用链式前向星,还有种是dfs的方式,之后会继续补充
分别给出两种代码
struct Edge{
int from,to,cap,flow;
// from:起始边, to:终点边 , cap: 容量 , flow: 流量
Edge(int u,int v,int c,int f):from(u),to(v),cap(c),flow(f){}//内建函数
};
struct EdmondsKarp{
int n,m;
vector<Edge> edges;// edges 用来存所有的边, 正向反向边都要存
vector<int> G[maxn];//用来记录第i个点的每条边的序号
int a[maxn];//用来存增广路上的可改变的值
int p[maxn];// 用来记录入-编号
void init(int n){
for(int i=0;i<n;i++)G[i].clear();
edges.clear();
}
void Addedge(int from, int to ,int cap){
// 边的添加很有技巧
因为可以反悔,所以 我们每次要存入反向边,反向边的容量为0
当然有时候,根据题意的不同有时反向边的容量不为0,那么就按照题目的给的数据进行存储
edges.push_back(Edge(from, to, cap, 0));
edges.push_back(Edge(to, from, 0, 0));
m = edges.size();
// 这个也有技巧,其实这个和链式前向星很像(两条变的存储cnt++,但也是连在一起的,这个也是)
每次将这个点连接的边的序号push进去
G[from].push_back(m-2);
G[to].push_back(m-1);
}
int maxflow(int s,int t){
int flow = 0;
for(;;){
CLR(a);
queue<int> Q;
Q.push(s);
a[s]=inf;
while(!Q.empty()){
int x = Q.front();
Q.pop();
for(int i=0;i<G[x].size();i++){
//访问这个点中的所有边
Edge& e = edges[G[x][i]];
// e得到这个边的信息
if(!a[e.to] && e.cap>e.flow){
//这个部分比较难理解
!a[e.to]最小残留量为0是才会更新,如果已经不为0说明更新过不再更新
当前的容量必须大于流量
p[e.to]=G[x][i];
// 储存当前点的标号,用以后面更新
a[e.to]=min(a[x],e.cap-e.flow);
Q.push(e.to);
}
}
if(a[t])break;//如果被更新break
}
if(!a[t])break;
//此时找不到增广路
for(int u=t;u != s;u = edges[p[u]].from){
edges[p[u]].flow += a[t];
edges[p[u]^1].flow -= a[t];
}
flow += a[t];
}
return flow;
}
};
下面给出用链式前向星代码
void addedge(int u,int v,int w){//这边就是一个简单的链式前向星处理
pnt[cnt]=v;
nxt[cnt]=head[u];
cap[cnt]=w;
head[u]=cnt++;
}
void adddouble(int u,int v,int w){//同样是加边,两条
addedge(u,v,w);
addedge(v,u,0);
}
int maxflow(int s,int t){
int flow = 0;
for(;;){
CLR(a);
queue<int> q;
q.push(s);
a[s]=inf;//不要忘记初始化为inf
while(!q.empty()){
int u = q.front();
q.pop();
for(int i=head[u]; ~i; i= nxt[i]){
int v =pnt[i];
if(!a[v] && cap[i] > 0){
a[v]=min(cap[i],a[u]);
pre[v]=i;// 此时的pre也是记录当前点的标号,记住用链式前向星的时候这是i!
q.push(v);
}
}
if(a[t])break;
}
if(!a[t])break;
flow += a[t];
for(int i,u=t;u!=s;u = pnt[i^1]){// 这部分的更新也是。要对链式前向星理解才行呢!
i = pre[u];
cap[i] -= a[t];//此时我省略了流量,只用了流量所以加减就是反着的,理解理解
cap[i^i] += a[t];
}
}
return flow;
}