一、最大流
我們有一張圖,要求從源點流向匯點的最大流量(可以有很多條路到達匯點),就是我們的最大流問題。EK算法模板如下
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int INF=0x7ffffff;
queue <int> q;
int n,m,x,y,s,t,g[201][201],pre[201],flow[201],maxflow;
//g鄰接矩陣存圖,pre增廣路徑中每個點的前驅,flow源點到這個點的流量
inline int bfs(int s,int t)
{
while (!q.empty()) q.pop();
for (int i=1; i<=n; i++) pre[i]=-1;
pre[s]=0;
q.push(s);
flow[s]=INF;
while (!q.empty())
{
int x=q.front();
q.pop();
if (x==t) break;
for (int i=1; i<=n; i++)
//EK一次只找一個增廣路
if (g[x][i]>0 && pre[i]==-1)
{
pre[i]=x;
flow[i]=min(flow[x],g[x][i]);
q.push(i);
}
}
if (pre[t]==-1) return -1;
else return flow[t];
}
//increase爲增廣的流量
void EK(int s,int t)
{
int increase=0;
while ((increase=bfs(s,t))!=-1)//這裏的括號加錯了!Tle
{//迭代
int k=t;
while (k!=s)
{
int last=pre[k];//從後往前找路徑
g[last][k]-=increase;
g[k][last]+=increase;
k=last;
}
maxflow+=increase;
}
}
int main()
{
scanf("%d%d%d%d",&n, &m, &s, &t);
for (int i=1; i<=m; i++)
{
int z;
scanf("%d%d%d",&x,&y,&z);
g[x][y]+=z;//此處不可直接輸入,要+=
}
EK(s,t);
printf("%d",maxflow);
return 0;
}
速度更快的Dinic算法模板如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int inf=1e9;
int n,m,x,y,z,s,t,maxflow,deep[500];//deep深度
struct Edge{
int next,to,dis;
}edge[500];
int num_edge=-1,head[500],cur[500];//cur用於複製head
queue <int> q;
void add_edge(int from,int to,int dis,bool flag)
{
edge[++num_edge].next=head[from];
edge[num_edge].to=to;
if (flag) edge[num_edge].dis=dis;//反圖的邊權爲 0
head[from]=num_edge;
}
//bfs用來分層
bool bfs(int s,int t)
{
memset(deep,0x7f,sizeof(deep));
while (!q.empty()) q.pop();
for (int i=1; i<=n; i++) cur[i]=head[i];
deep[s]=0;
q.push(s);
while (!q.empty())
{
int now=q.front(); q.pop();
for (int i=head[now]; i!=-1; i=edge[i].next)
{
if (deep[edge[i].to]>inf && edge[i].dis)//dis在此處用來做標記 是正圖還是返圖
{
deep[edge[i].to]=deep[now]+1;
q.push(edge[i].to);
}
}
}
if (deep[t]<inf) return true;
else return false;
}
//dfs找增加的流的量
int dfs(int now,int t,int limit)//limit爲源點到這個點的路徑上的最小邊權
{
if (!limit || now==t) return limit;
int flow=0,f;
for (int i=cur[now]; i!=-1; i=edge[i].next)
{
cur[now]=i;
if (deep[edge[i].to]==deep[now]+1 && (f=dfs(edge[i].to,t,min(limit,edge[i].dis))))
{
flow+=f;
limit-=f;
edge[i].dis-=f;
edge[i^1].dis+=f;
if (!limit) break;
}
}
return flow;
}
void Dinic(int s,int t)
{
while (bfs(s,t))
maxflow+=dfs(s,t,inf);
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d%d%d%d",&n,&m,&s,&t);
for (int i=1; i<=m; i++)
{
scanf("%d%d%d",&x,&y,&z);
add_edge(x,y,z,1); add_edge(y,x,z,0);
}
Dinic(s,t);
printf("%d",maxflow);
return 0;
}
二、最小費用最大流
假如我們有一個流量網絡,現在每個邊除了流量,現在還有一個單位費用,這條邊的費用相當於它的單位費用乘上它的流量,我們要保持最大流的同時,還要保持邊權最小,這就是最小費用最大流問題。 因爲在一個網絡流圖中,最大流量只有一個,但是“流法”有很多種,每種不同的流法所經過的邊不同因此費用也就不同,所以需要用到最短路算法。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn=100010;
bool vis[maxn];
int n,m,s,t,x,y,z,f,dis[maxn],pre[maxn],last[maxn],flow[maxn],maxflow,mincost;
//dis最小花費;pre每個點的前驅;last每個點的所連的前一條邊;flow源點到此處的流量
struct Edge{
int to,next,flow,dis;//flow流量 dis花費
}edge[maxn];
int head[maxn],num_edge;
queue <int> q;
void add_edge(int from,int to,int flow,int dis)
{
edge[++num_edge].next=head[from];
edge[num_edge].to=to;
edge[num_edge].flow=flow;
edge[num_edge].dis=dis;
head[from]=num_edge;
}
bool spfa(int s,int t)
{
memset(dis,0x7f,sizeof(dis));
memset(flow,0x7f,sizeof(flow));
memset(vis,0,sizeof(vis));
q.push(s); vis[s]=1; dis[s]=0; pre[t]=-1;
while (!q.empty())
{
int now=q.front();
q.pop();
vis[now]=0;
for (int i=head[now]; i!=-1; i=edge[i].next)
{
if (edge[i].flow>0 && dis[edge[i].to]>dis[now]+edge[i].dis)//正邊
{
dis[edge[i].to]=dis[now]+edge[i].dis;
pre[edge[i].to]=now;
last[edge[i].to]=i;
flow[edge[i].to]=min(flow[now],edge[i].flow);//
if (!vis[edge[i].to])
{
vis[edge[i].to]=1;
q.push(edge[i].to);
}
}
}
}
return pre[t]!=-1;
}
void MCMF()
{
while (spfa(s,t))
{
int now=t;
maxflow+=flow[t];
mincost+=flow[t]*dis[t];
while (now!=s)
{//從源點一直回溯到匯點
edge[last[now]].flow-=flow[t];//flow和dis容易搞混
edge[last[now]^1].flow+=flow[t];
now=pre[now];
}
}
}
int main()
{
memset(head,-1,sizeof(head)); num_edge=-1;//初始化
scanf("%d%d%d%d",&n,&m,&s,&t);
for (int i=1; i<=m; i++)
{
scanf("%d%d%d%d",&x,&y,&z,&f);
add_edge(x,y,z,f); add_edge(y,x,0,-f);
}
MCMF();
printf("%d %d",maxflow,mincost);
return 0;
}
三、最小割
割其實就是刪邊的意思,當然最小割就是割掉X條邊來讓S跟T不互通。我們要求X條邊加起來的流量綜合最小。這就是最小割問題。最小割等於最大流。