191107-网络流练习
一,最大流问题
1),Edmonds-Karp算法
时间复杂度为 O ( n m 2 ) O(nm^2) O(nm2) 一般能处理 1 0 3 − 1 0 4 10^3-10^4 103−104规模的网络
模板
#include<bits/stdc++.h>
#define M 200006
const int inf=1e8;
int maxn,n,m,s,t,nxt[M*2],first[M],to[M*2],w[M*2],incf[M],pre[M],tot=1;//tot要从1开始
bool vis[M];
using namespace std;
void add(int x,int y,int z){
nxt[++tot]=first[x],first[x]=tot,to[tot]=y,w[tot]=z;
nxt[++tot]=first[y],first[y]=tot,to[tot]=x,w[tot]=0;
}
bool bfs(){
memset(vis,0,sizeof(vis));
queue<int>q;
q.push(s);
vis[s]=1;
incf[s]=inf;
while(!q.empty()){
int v=q.front();
q.pop();
for(int i=first[v];i;i=nxt[i]){
if(w[i]){
int u=to[i];
if(vis[u]) continue;
incf[u]=min(incf[v],w[i]);
pre[u]=i;
q.push(u);
vis[u]=1;
if(u==t) return true;
}
}
}
return false;
}
void update(){
int v=t;
while(v!=s){
int u=pre[v];
w[u]-=incf[t];
w[u^1]+=incf[t];
v=to[u^1];
}
maxn+=incf[t];
}
int main(){
int x,y,z;
scanf("%d%d%d%d",&n,&m,&s,&t);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
}
while(bfs())
update();
printf("%d",maxn);
return 0;
}
2),Dinic算法
时间复杂度为 O ( n 2 m ) O(n^2m) O(n2m),一般能处理 1 0 4 − 1 0 5 10^4-10^5 104−105规模的网络
模板
#include<bits/stdc++.h>
#define M 200006
using namespace std;
const int inf=1e8;
int to[M*2],nxt[M*2],w[M*2],first[M],tot=1,d[M],now[M],n,m,s,t,ans;
void add(int x,int y,int z){
nxt[++tot]=first[x],first[x]=tot,to[tot]=y,w[tot]=z;
nxt[++tot]=first[y],first[y]=tot,to[tot]=x,w[tot]=0;
}
bool bfs(){
memset(d,0,sizeof(d));
queue<int>q;
q.push(s);
d[s]=1;
now[s]=first[s];
while(q.size()){
int v=q.front();
q.pop();
for(int i=first[v];i;i=nxt[i]){
int u=to[i];
if(w[i]&&!d[u]){
q.push(u);
now[u]=first[u];
d[u]=d[v]+1;
if(u==t) return 1;
}
}
}
return 0;
}
int dinic(int x,int flow){
if(x==t) return flow;
int rest=flow,k,i;
for(i=now[x];i&&rest;i=nxt[i]){
int u=to[i];
if(w[i]&&d[u]==d[x]+1){
k=dinic(u,min(rest,w[i]));
if(!k) d[u]=0;
w[i]-=k;
w[i^1]+=k;
rest-=k;
}
}
now[x]=i;
return flow-rest;
}
int main(){
int flow=0,x,y,z;
scanf("%d%d%d%d",&n,&m,&s,&t);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
}
while(bfs())
while(flow=dinic(s,inf)) ans+=flow;
printf("%d",ans);
return 0;
}
3),解决二分图最大匹配问题
解析
建立一个源点和汇点,从源点S到做左部点连一条有向边,从每个右部点向汇点T连一条有向边,网络中每条边的容量都为1,最大流即为最大匹配数
题解
二,最小割问题
三,费用流
最小费用最大流
模板 (dinic)
#include<bits/stdc++.h>
#define M 200006
using namespace std;
const int inf=1e8;
int f[M*2],w[M*2],to[M*2],first[M],nxt[M*2],tot=1,d[M],n,m,s,t,ans,ret,now[M],ans1;
bool vis[M];
void add(int x,int y,int z,int v){
nxt[++tot]=first[x];
first[x]=tot;
to[tot]=y;
w[tot]=z;
f[tot]=v;
}
bool spfa(){
for(int i=1;i<=n;i++) d[i]=inf;
memset(vis,0,sizeof(vis));
memcpy(now,first,sizeof(first));
queue<int>q;
q.push(s);
vis[s]=1;
d[s]=0;
while(q.size()){
int v=q.front();
q.pop();
vis[v]=0;
for(int i=first[v];i;i=nxt[i]){
int u=to[i];
if(w[i]&&d[u]>d[v]+f[i]){
d[u]=d[v]+f[i];
if(!vis[u]){
q.push(u);
vis[u]=1;
}
}
}
}
return d[t]!=inf;
}
int dfs(int r,int flow){
if(t==r) return flow;
vis[r]=1;
int ans=0,i;
for(i=now[r];i&&ans<flow;i=nxt[i]){
int u=to[i];
if(!vis[u]&&w[i]&&d[u]==d[r]+f[i]){
int x=dfs(u,min(w[i],flow-ans));
if(x){
ret+=f[i]*x;
w[i]-=x;
w[i^1]+=x;
ans+=x;
}
}
}
now[r]=i;//可以开当前弧优化
vis[r]=0;
return ans;
}
int main(){
int x,y,z,v;
scanf("%d%d%d%d",&n,&m,&s,&t);
for(int i=1;i<=m;i++){
scanf("%d%d%d%d",&x,&y,&z,&v);
add(x,y,z,v);
add(y,x,0,-v);
}
int flow=0;
while(spfa()){
memset(vis,0,sizeof(vis));//清不清0都能过?
while(flow=dfs(s,inf)) ans1+=flow;
}
printf("%d %d",ans1,ret);
return 0;
}