(就是想来理一理思路。小菜鸡第一次写题解(可能也不算是题解),之前都只是做一做题的。)
(语言可能描述的不到位或者不清楚(感觉有些地方说不太清呀~~~,然后也不太会写题解),错误的地方请大家指出)
存下图,建立反向边,构建残余网络,在残余网络中不断寻找增广路径,维护残余网络,将其累加至答案中,直到找不到增广路径。
1~n的节点之间的边通过add函数来记录,边的编号从2开始。
在点s之前新增一条编号为1的边,起点是一个新增的点(编号是多少没有用到,所以也没有管它.jpg),终点为s(这样是为了写bfs的代码时候方便一些,不用先处理以s为顶点的边,直接从编号为1的这条边开始就好)
代码中有注释~~~
代码如下:
#include<iostream>
#include<cstring>
using namespace std;
const int N=1010,M=20010;
//h[i]存下以点i为起点的边的编号(按输入顺序,存下的是最后一条边的编号)
//f[i]存下边i的权值,e[i]存下边i的终点,ne[i]存下和边i同起点的上一条边的编号(可以依次找出同起点的所有边)
//idx用来记录边的编号
//p,d,pre三个数组在bfs寻找增广路径时使用,来记录当前搜到的边的信息,
//p[i]表示当前边的编号,d[i]表示走到这条边的路径中最小的边权值(最大的可行流量),
//pre[i]表示当前这一条边是从之前的哪一条走过来的(记录的是bfs过程中的相应下标)
//tot用来记录bfs过程中展开的边的数量,下标为tot的位置是增广路径的最后一条边(其终点为t)
//n,m,s,t如题,mark用来标记bfs过程中已经到达了的点,防止重复搜索
int h[N]={0},f[M]={0},e[M]={0},ne[M]={0},idx=2;
int p[N]={0},d[N]={0},pre[N]={0},tot=0,n=0,m=0,s=0,t=0;
bool mark[N]={0};
//存下边的信息,同时要存下反向边,构建残余网络
void add(int& a,int& b,int& c){
e[idx]=b;f[idx]=c;ne[idx]=h[a];h[a]=idx;idx++;
e[idx]=a;f[idx]=0;ne[idx]=h[b];h[b]=idx;idx++;
}
//bfs找增广路径,结果记录在p,d,pre三个数组中
bool bfs(){
int l=0;tot=0;//l是左端点,tot相当于右端点(每次重新置为0)
memset(mark,0,sizeof(mark));//mark数组赋为false
while(l<=tot){ //l<=tot,还要继续展开边
//p[l]表示当前这条边的编号,e[p[l]]表示当前这条边的终点,h[e[p[l]]]表示以这一终点为顶点的一条边
//通过ne数组不断找到以这一终点为顶点的所有边,for循环中的i就是这些边的编号
for(int i=h[e[p[l]]];i;i=ne[i]){ //h数组的初始值为0,当i为0时表示以e[p[l]]为起点的边都搜过了
if(!mark[e[i]]&&f[i]){//如果当前边的终点还没有到达过并且当前这条边的权值大于0,就会展开这一条边
tot++;//右边界++,并标记上这条边的终点
mark[e[i]]=1;
//存下这条边,p[tot]存下编号i,
//d[tot]存下路径中最小的权值(到达上一条边时最小权值d[l]和当前边i的权值中的最小值)
//pre[tot]存下当前这一边是从下标l位置的那条边走过来的
p[tot]=i;d[tot]=min(f[i],d[l]);pre[tot]=l;
if(e[i]==t)return true;//当前这条边到达了终点,返回true
}
}
l++;// l不要忘记++(因为我就忘了),继续展开下一条边
}
return false;//没有找到增广路径辽~
}
int main(){
std::ios::sync_with_stdio(false);
cin>>n>>m>>s>>t;
e[1]=s;//添加编号为1的边,其终点为s(边的其他信息没有管它,因为没有用到~~
p[0]=1;d[0]=1e9;//bfs的起点是这条编号为1的边,当前最小权值赋成一个较大的值
int a=0,b=0,c=0;
while(m--){//输入边的信息,存下来
cin>>a>>b>>c;
add(a,b,c);
}
int ans=0;
while(bfs()){//当前残网络中还可找到增广路径,则累加至ans中,并修改这条路径中正向边和反向边的权值
ans+=d[tot];
for(int i=tot;i;i=pre[i]){//从tot位置的边开始,沿着增广路径往回走,走到0位置的边(也就是边1时)停止
//路径中的每一条边减去d[tot],其反向边加上d[tot]
f[p[i]]-=d[tot];f[p[i]^1]+=d[tot];
}
}
cout<<ans<<endl;
return 0;
}
楼主,为什么反向边是p[i]^1呀
因为正向边和反向边的编号刚好就是异或一个1的差别~
2^1=3,4^1=5,6^1=7,…
#奆!
必须得赞一个
赞同!
这注释无敌hhh
抓住lemmon_kk
抓住lemmon_kk