EK算法求最大流:
BFS找增广路增广直到无法增广
复杂度:O(V*E^2)
核心代码(邻接表存图):(一开始学最大流的时候写的代码,非常丑)
int pre[maxn];
int vis[maxn]={0};
bool bfs(){//bfs求路径
for(int i = 0;i <= n+1;++i) vis[i] = 0;
queue q;q.push(0);vis[0] = 1;
while(q.size()){
int u = q.front();q.pop();
for(int i = head[u];i!=-1;i = e[i].nxt){
int v = e[i].v;
if(vis[v]) continue;
if(e[i].f == 0) continue;
vis[v] = 1;
pre[v] = i;
q.push(v);
}
}
return vis[n+1];
}
while(bfs()){//更新正向边和反向边的流量
ll d = inf;
int u,v;
v = n+1;
while(v!=0){
u = e[pre[v]].u;
d = min(d,e[pre[v]].f);
v = u;
}
v = n+1;
while(v != 0){
u = e[pre[v]].u;
if(pre[v]&1) mp[u][v]-=d;
else mp[u][v]+=d;
e[pre[v]].f -= d;
e[pre[v]^1].f += d;//给反向边加上回流
v = u;
}
ans += d;
}
Dinic算法求最大流:
先用BFS求图的层次,然后DFS根据BFS求得的深度增广(只走深度比当前结点大1的点)
复杂度:O(V^2 * E)
核心代码:
int dep[maxn];
int q[maxn*2];
int tot,tail;
bool bfs(){
memset(dep,-1,(ex+1)<<2);
dep[st] = 1;
q[tot = 0] = st,tail = 1;
while(tot < tail){
int u = q[tot++];
if(u == ed) break;
for(int i = head[u];~i;i = e[i].nxt){
int v = e[i].v;
if(dep[v]!=-1 || !e[i].f) continue;
dep[v] = dep[u] + 1;
q[tail++] = v;
}
}
return dep[ed]!=-1;
}
int cur[maxn];
ll dfs(int u,ll flow){
ll res = flow;
if(u == ed) return flow;
for(int &i = cur[u];~i;i = e[i].nxt){//弧优化
int v = e[i].v;
if(dep[v]!=dep[u] + 1 || !e[i].f) continue;
int d = dfs(v,min(res,e[i].f));
e[i].f -= d;
e[i^1].f += d;
res -= d;
if(res == 0) break;
}
if(flow == res) dep[u] = -1;//防止重新搜索
return flow - res;
}
int dinic(){
ll ans = 0;
int d;
while(bfs()){
for(int i = 0;i <= ex;++i) cur[i] = head[i];
while(d = dfs(st,inf)) ans += d;
}
return ans;
}
最小费用流:
每次增广用spfa算法求得最短增广路,直到满流
复杂度分析:O(spfa算法复杂度*E)并不严谨
核心代码:
int pre[maxn];
int flow[maxn];
int inq[maxn];
int dis[maxn*maxn];
int spfa(){//获取最短增广路
for(int i = 0;i <= ed;++i) pre[i] = -1,dis[i] = inf,inq[i] = 0;
queue q;
q.push(st);
inq[st] = 1;
dis[st] = 0;
flow[st] = inf;
while(q.size()){
int u = q.front();q.pop();
inq[u] = 0;
for(int i = head[u];i != -1;i = e[i].nxt){
int v = e[i].v;
if(!e[i].f) continue;
if(dis[v] > dis[u] + e[i].w){
dis[v] = dis[u] + e[i].w;
flow[v] = min(e[i].f,flow[u]);
pre[v] = i;
if(!inq[v]) inq[v] = 1,q.push(v);
}
}
}
if(dis[ed] == inf) return -1;
return flow[ed];
}
int mfmv(){
int ans = 0;
int d;
int sumd = 0;
while((d = spfa())!=-1){
ans += dis[ed]*d;
sumd += d;
int v = ed;
while(v!=st){
e[pre[v]].f -= d;
e[pre[v]^1].f += d;
v = e[pre[v]].u;
}
}return ans;
}
1.无汇源可行流:
建立超级源点和汇点S和T,原图中每条边的容量变为(上限 - 下限),并记录每个点下限流量的流入和流出,S连向流入 > 流出,流入< 流出的点连向T,容量都为|流入-流出|,跑最大流,若最大流*2 = |流入-流出|的总和,则有可行流。
复杂度:最大流算法的复杂度
关键代码:(建图,判断)
while(m--){
int u,v;
ll low,up;
scanf("%d%d%lld%lld",&u,&v,&low,&up);
add(u,v,up-low);
du[u] -= low;
du[v] += low;
}
for(int i = 1;i <= n; ++i){
if(du[i] > 0) sum += du[i],add(st,i,du[i]);
else if(du[i] < 0) add(i,ed,-du[i]);
}
//判断语句↓
ll t = dinic();
if(t < sum) {
cout<<"没有可行流;
}
2.有汇源的可行流
在汇点和源点连一条容量inf的边,转换成问题 1.无汇源可行流
3.有汇源最大可行流:
建超级源点和超级汇点按问题1跑一遍可行流,删掉汇点到源点的那条inf的边,超级源点设置为普通源点,超级汇点设置成普通汇点,再跑一次最大流。
复杂度:最大流算法复杂度
关键代码:
void sol(){
ll t = dinic();
if(t < sum) {
cout<<"不存在可行流\n";return;
}
e[cnt-2].f = 0;
st = S;ed = T;
t = dinic();
cout<
4.有汇源最小可行流:
建超级源点和超级汇点按问题1跑一遍可行流,删掉汇点到源点的那条inf的边,超级源点设置为普通汇点,超级汇点设置成普通源点,再跑一次最大流(得到可以回流的最大流量),答案就是可行流减去最大流。
关键代码:
void sol(){
ll t = dinic();
if(t < sum) {
cout<<"没有可行流\n";return;
}
t = e[cnt-1].f;
e[cnt-2].f = 0;
st = T;ed = S;
t = t - dinic();
cout<
5.最小费用可行流
按照问题1的方式建图,跑最小费用流,并检查得到的最大流是否为可行流。
代码:(CCF201812-5 管道清洁)
#include
#include
#include
using namespace std;
const int maxn = 233;
const int inf = 0x3f3f3f3f;
struct node{
int u,v,f,w,nxt;
node(){};
node(int a,int b,int c,int d,int e):u(a),v(b),f(c),w(d),nxt(e){}
}e[maxn*maxn];
int cnt = 0;
int head[maxn];
void add(int u,int v,int f,int w){
e[cnt] = node(u,v,f,w,head[u]);
head[u] = cnt++;
e[cnt] = node(v,u,0,-w,head[v]);
head[v] = cnt++;
}
int st,ed,E,ex;
int n,m;
int du[maxn];
int sum;
int num = 0;
void init(){
scanf("%d%d",&n,&m);
st = 0;ed = n + 1;
ex = n + 2;
cnt = 0;
sum = 0;
num = 0;
memset(head,-1,ex<<2);
memset(du,0,ex<<2);
while(m--){
int u,v;char c[2];
scanf("%d%d%s",&u,&v,c);
if(c[0] == 'A'){
add(u,v,inf,E);
du[u]--;
du[v]++;
num+=E;
}
else if(c[0] == 'B'){
du[u]--;
du[v]++;
num+=E;
}
else if(c[0] == 'C'){
add(u,v,inf,E);
}
else add(u,v,1,E);
}
for(int i = 1;i <= n;++i){
if(du[i] > 0){
sum += du[i];
add(st,i,du[i],0);
}
else if(du[i] < 0){
add(i,ed,-du[i],0);
}
}
}
int pre[maxn];
int flow[maxn];
int inq[maxn];
int dis[maxn];
int spfa(){
memset(pre,-1,ex<<2);
memset(inq,0,ex<<2);
memset(dis,0x3f,ex<<2);
queue q;q.push(st);
flow[st] = inf;inq[st] = 1;
dis[st] = 0;
while(q.size()){
int u = q.front();q.pop();
inq[u] = 0;
for(int i = head[u];i != -1; i = e[i].nxt){
int v = e[i].v;
if(e[i].f && dis[v] > dis[u] + e[i].w){
dis[v] = dis[u] + e[i].w;
pre[v] = i;
flow[v] = min(flow[u],e[i].f);
if(!inq[v]) inq[v] = 1,q.push(v);
}
}
}
if(pre[ed] == -1) return -1;
return flow[ed];
}
int mfmv(){
int fw = 0;
int ans = 0;
int d;
while((d = spfa())!=-1){
fw += d;
ans += dis[ed]*d;
int v = ed;
while(v!=st){
e[pre[v]].f -= d;
e[pre[v]^1].f += d;
v = e[pre[v]].u;
}
}
if(fw!=sum) return -1;
return ans + num;
}
void sol(){
int ans = mfmv();
printf("%d\n",ans);
}
int main(){
int T;cin>>T;cin>>E>>E;
while(T--){
init();sol();
}
}