图论 最大流EK算法

今天接触了最大流,网上有很多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;
}







  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值