最大流
我再次博客上暂不叙述预留推进(SAP),事实是,我不会,暂且搁置,以后会补充
最大流在实际问题中的应用是,你要从一个源点s送水至汇点t,这些点中有水管链接,水管的最大能通过的水有不同,让你求单位时间内你最多能送多少水。
我们的流网络可以理解为一群有向边图
一些想法
我们定义\(f(u,v)\)为u节点到v节点的流量。,\(c(u,v)\)为u节点到v节点的流量限制
首先我们由于不能爆水管,有这个显而易见的式子
\[ 0 \le f(u,v) \le c(u,v) \]
其次我们来,思考一下对于每个节点的的流出和流入,显然的我们可以把KCL(即Kirchhoff's Current Law),推广到oi界上,我们有
\[ \sum _{v\in V} f(v,u)=\sum_{v \in V} f(u,v) \]
想法结束进入正题
经过度娘的帮助,我们知道一种叫做增广路的东西,在一个残余网络(就是说某些管子已经有水了),就是说从源节点到汇节点的一条能走水的路径
这样我们知道,假如,我们一遍又一遍的操作之后,没有增广路可以找了,那么我们必定求出了最大流
然而怎么寻找增广路,这是一个问题。
经过思考之后我们可以用一个十分暴力的思想来解决,进行深搜,主要思想是
- 我们可以每一次从原点开始深搜
- 搜到汇节点,返回增广路上的最小权值、
- 重复第二步,知道找不到增广路
我们的具体实现,建反向边,把正的流顶回去的操作
具体实现
#include
#include
#include
#include
using namespace std;
const int Maxn=201;
int n,m,min1,s,ans,t,u,v,w,Map[Maxn][Maxn],pre[Maxn];
bool flag[Maxn];
void dfs(int u){
flag[u]=1;
if(u==t) return ;
for(int i=1;i<=n;i++){
if(!flag[i]&&Map[u][i]>0){
pre[i]=u;
dfs(i);
if(flag[t]){
min1=min(min1,Map[u][i]);
return ;
}
}
}
return ;
}
int main(){
// freopen("flow.in","r",stdin);
scanf("%d%d%d%d",&n,&m,&s,&t);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&u,&v,&w);
Map[u][v]+=w;
}
while(1){
// printf("9999:\n");
memset(pre,0,sizeof pre);
memset(flag,0,sizeof flag);
min1=0x3f3f3f3f;
dfs(s);
if(!flag[t]) break;
int now=t;
while(1){
if(now==s) break;
Map[pre[now]][now]-=min1;
Map[now][pre[now]]+=min1;
now=pre[now];
}
}
printf("%d",ans);
return 0;
}
值得一提的有两点
- 如果你拿矩阵存,要对重边处理
- dfs 的类型。。。
有兴趣去翻翻历史,EK和Dinic的渊源
好了我们开始有一点DINIC的雏形了,dinic的优秀是在,他做了一个叫层次网络的东西,可以大大加快速度
我们来说说Dinic算法的套路
- 对不饱和边进行广搜
- 搜到之后 按照上面的套路做深搜,要加一下是否满足层次网络
- 计算答案
- 返回1步,能搜到t则继续,否则退出循环
而且有两个简单优化
- 当前弧
- 满流 ,好像是
我上代码了
#include
#include
#include
#include
using namespace std;
const int Maxn=2*1e4+11,Maxm=2*1e5+11;
struct Edge{
int fr,to,lac,wg;
}edge[Maxm];
int s,t,n,m,u,v,w,cnt,h[Maxn],dep[Maxn],ans,cur[Maxn];
bool flag[Maxn],vis[Maxn];
int read(){
int x=0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch-'0');
ch=getchar();
}
return x;
}
void insert(int u,int v,int w){
edge[cnt].to=v;
edge[cnt].lac=h[u];
edge[cnt].fr=u;
edge[cnt].wg=w;
h[u]=cnt++;
}
void bfs(int u){
queueq1;
q1.push(u);
dep[u]=0,flag[u]=1;
while(!q1.empty()){
int fr=q1.front();
q1.pop();
for(int i=h[fr];i!=-1;i=edge[i].lac){
int to=edge[i].to;
if((!edge[i].wg)||flag[to]) continue;
q1.push(to);
dep[to]=dep[fr]+1;
flag[to]=1;
}
}
}
int dfs(int u,int min1){
if(u==t) return min1;
int sum=min1;
flag[u]=1;
for(int i=cur[u];i!=-1;i=edge[i].lac){
int to=edge[i].to;
if(edge[i].wg==0) continue;
if(dep[to]!=dep[u]+1) continue;
if(flag[to]) continue;
cur[u]=i;//当前弧
int ret1=dfs(to,min(sum,edge[i].wg));
edge[i].wg-=ret1,edge[i^1].wg+=ret1;
sum-=ret1;
if(!sum) break;//满流
}
return min1-sum;
}
int main(){
// freopen("Dinic.in","r",stdin);
n=read(),m=read(),s=read(),t=read();
memset(h,-1,sizeof h);
for(int i=1;i<=m;i++){
u=read(),v=read(),w=read();
insert(u,v,w);
insert(v,u,0);
}
while(1){
memset(dep,-1,sizeof dep);
memset(flag,0,sizeof flag);
memcpy(cur,h,sizeof cur);
bfs(s);
if(dep[t]==-1) break;
memset(flag,0,sizeof flag);
int ret=dfs(s,0x3f3f3f3f);
ans+=ret;
}
printf("%d",ans);
return 0;
}
值得一提的是 用^,天下我有,
然后优化,一定要加,前人的经验呀,,,血与泪的教训
最小费用最大流
SFPA
把深搜和广搜,改成某个知名的的单源最短路求法就好了 ,慎用,f是经验
#include
#include
#include
#include
using namespace std;
const int Maxm=150001,Maxn=5005;
struct Edge{
int to,flo,co,lac;//co指cost ,flo 指 flow
}edge[Maxm];
int n,m,h[Maxn],s,t,x,y,z,f,cnt,co[Maxn],flow[Maxn],pre[Maxn],maxflo,minco,last[Maxn];
bool vis[Maxn];
void insert(int x,int y,int z,int f){
edge[cnt].to=y;
edge[cnt].lac=h[x];
edge[cnt].flo=z;
edge[cnt].co=f;
h[x]=cnt++;
}
bool SPFA(int s,int t){
memset(flow,0x3f3f3f3f,sizeof flow);
pre[t]=-1;
memset(co,0x3f3f3f3f,sizeof co);
memset(vis,0,sizeof vis);
queue q;
q.push(s);vis[s]=1;co[s]=0,pre[s]=-1;
while(!q.empty()){
int fr=q.front();
q.pop();
vis[fr]=0;
for(int i=h[fr];i!=-1;i=edge[i].lac){
int to=edge[i].to;
if(edge[i].flo>0&&co[to]>co[fr]+edge[i].co){
co[to]=co[fr]+edge[i].co;
pre[to]=fr;
last[to]=i;
flow[to]=min(flow[fr],edge[i].flo);
if(!vis[to]){
q.push(to);
vis[to]=1;
}
}
}
}
return pre[t]!=-1;
}
void SFPA(){
while(SPFA(s,t)){
int now=t;
maxflo+=flow[t];
minco+=flow[t]*co[t];
while (now!=s){
edge[last[now]].flo-=flow[t];
edge[last[now]^1].flo+=flow[t];
now=pre[now];
}
}
}
int main() {
// freopen("SFPA.in","r",stdin);
memset(h,-1,sizeof h);
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);
insert(x,y,z,f);
insert(y,x,0,-f);
}
SFPA();
printf("%d %d",maxflo,minco);
return 0;
}
这个算法是用了广搜的思想,Dinic的骨架,其正确性来源于流量优先,而费用次之
注意记录前驱
咕,以后还会增加内容的,我累了。。
记于2020年2月1日