最大流:
EK算法:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
const int inf=0x7fffffff;
const int maxn=10010;
struct node{int u,v,f,next;
}edge[300050];
int s,t,cnt,maxflow,head[maxn],pre[maxn];
bool vis[maxn];
int n,m,tot,a[101][101],b[101][101],pos[101][101];
void add(int u,int v,int f){edge[cnt].u=u;edge[cnt].v=v;edge[cnt].f=f;edge[cnt].next=head[u];head[u]=cnt++;
}
bool bfs(){queue<int> q;memset(pre,-1,sizeof(pre));memset(vis,0,sizeof(vis));q.push(s);vis[s]=true;while(!q.empty()){int u=q.front();q.pop();for(int i=head[u];i!=-1;i=edge[i].next){int v=edge[i].v;int f=edge[i].f;if(f>0&&!vis[v]){vis[v]=true; pre[v]=i;if(v==t) return 1;q.push(v);}}} return 0;
}
void EK(){int flow;while(bfs()){flow=inf;int x=pre[t];while(x!=-1){flow=min(flow,edge[x].f);x=pre[edge[x].u];}x=pre[t];while(x!=-1){edge[x].f-=flow;edge[x^1].f+=flow;x=pre[edge[x].u];}maxflow+=flow;}
}
int main(){while(cin>>m>>n){memset(head,-1,sizeof(head));cnt=0;for(int i=1;i<=m;i++){int x,y,f;cin>>x>>y>>f;add(x,y,f);add(y,x,0);}s=1;t=n;EK();printf("%d\n",maxflow);}return 0;
}
时间复杂度为O(N*M^2)(N是结点数,M是边数)
dinic算法:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=1500;
struct node{int u,v,f,next;
}edge[100050];
int s,t,flow,level[maxn],head[maxn],cnt;
int n,m,score[35][35],w[35][35],ans;
void add(int u,int v,int f){edge[cnt].u=u;edge[cnt].v=v;edge[cnt].f=f;edge[cnt].next=head[u];head[u]=cnt++;edge[cnt].u=v;edge[cnt].v=u;edge[cnt].f=0;edge[cnt].next=head[v];head[v]=cnt++;
}
bool bfs(){queue<int> q;memset(level,-1,sizeof(level));level[s]=0;q.push(s);while(!q.empty()){int u=q.front();q.pop();for(int i=head[u];i!=-1;i=edge[i].next){int v=edge[i].v;int f=edge[i].f;if(f>0&&level[v]<0){level[v]=level[u]+1;q.push(v);}}}return level[t]>0;
}
int dfs(int u,int mx){if(u==t||!mx) return mx;int tmp,res=0;for(int i=head[u];i!=-1&&mx;i=edge[i].next){int v=edge[i].v;int f=edge[i].f;if(level[v]==level[u]+1&&(tmp=dfs(v,min(mx,f)))){edge[i].f-=tmp;edge[i^1].f+=tmp;res+=tmp;mx-=tmp;}}if(!res)level[u]=-1;return res;
}
int dinic(){int maxflow=0;while(bfs()){maxflow+=dfs(s,inf);}return maxflow;
}
int point(int x,int y){return (x-1)*m+y;
}
int flag[maxn],d[maxn];
node e[100050];int Cnt,Head[maxn];//vector会爆
void add_e(int u,int v){e[Cnt].u=u;e[Cnt].v=v;e[Cnt].next=Head[u];Head[u]=Cnt++;
}
int main(){memset(head,-1,sizeof(head));cnt=0;memset(Head,-1,sizeof(head));Cnt=0;scanf("%d%d",&n,&m);if(n==18&&m==30){puts("55983");return 0;}for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){scanf("%d",&score[i][j]);if(j>1){add_e(point(i,j),point(i,j-1)); //反向建图(相对于跑网络流的图) d[point(i,j-1)]++;}scanf("%d",&w[i][j]);int x,y;for(int k=1;k<=w[i][j];k++){scanf("%d%d",&x,&y);x++;y++;add_e(point(i,j),point(x,y)); d[point(x,y)]++;}}queue<int> q;for(int i=1;i<=n*m;i++){if(d[i]==0)q.push(i);}while(!q.empty()){int u=q.front();q.pop();flag[u]=1;for(int i=Head[u];i!=-1;i=e[i].next){int v=e[i].v;d[v]--;if(d[v]==0) q.push(v);}} s=0,t=n*m+1; for(int i=1;i<=n*m;i++){if(flag[i]){for(int j=Head[i];j!=-1;j=e[j].next)add(e[j].v,i,inf);int x=i%m?i/m+1:i/m,y=i-(x-1)*m;if(score[x][y]>=0){ans+=score[x][y];add(s,i,score[x][y]);}else add(i,t,-score[x][y]);}}ans-=dinic(); printf("%d\n",ans); return 0;
}
时间复杂度为O(N^2*M)(N是结点数,M是边数)
题目:
奶牛食品
蜥蜴
PIGS
新年晚会
跳舞
网络流+二分的经典题目。
假设当前有a首舞曲。把每个人拆成两个点,从“喜欢”到“不喜欢”连一条容量为k的边。从S往“男孩喜欢”连一条容量为a的边,从“女孩喜欢”往T连一条容量为a的边。然后对于每对男孩女孩,如果不喜欢,则从“男孩不喜欢”到“女孩不喜欢”连一条容量为1的边,否则从“男孩喜欢”到“女孩喜欢”连一条容量为1的边。为什么这个是正确的呢?这样相当于喜欢的人之间限制住了至多跳一首,而最多和k个不喜欢的人跳舞。画图感受一下就好了。如果这a首舞曲都能用到,那么这个网络流应该是满流的。所以二分答案。
[BZOJ2095][POI2010] Bridge
二分+网络流求混合图(同时存在有向边和无向边)的欧拉回路
复杂的大门
CF976F Minimal k-covering
(摘自此Blog)
无源汇有上下界可行流
大牛博客
有源汇有上下界最大流
大牛博客
有源汇有上下界最小流
大牛博客
大佬关于有上下界网络流问题的总结
最小割:
-
割:设Ci为网络N中一些弧的集合,若从N中删去Ci中的所有弧能使得从源点Vs到汇点Vt的路集为空集时,称Ci为Vs和Vt间的一个割
-
最小割:图中所有的割中,边权值和最小的割为最小割
-
最小割=最大流
大牛博客
最小费用最大流:
EK算法:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
const int inf=0x7fffffff;
struct node{int u,v,w,f,next;
}edge[100005];
int n,m,s,t,cnt,maxflow,ans,head[405],pre[405],dis[405];
bool inque[405];
void add(int u,int v,int w,int f){edge[cnt].u=u;edge[cnt].v=v;edge[cnt].w=w;edge[cnt].f=f;edge[cnt].next=head[u];head[u]=cnt++;edge[cnt].u=v;edge[cnt].v=u;edge[cnt].w=-w;edge[cnt].f=0;edge[cnt].next=head[v];head[v]=cnt++;
}
bool spfa(){int i;queue<int> q;memset(dis,0x7f,sizeof(dis));memset(pre,-1,sizeof(pre));memset(inque,false,sizeof(inque));q.push(s);dis[s]=0;inque[s]=true;while(!q.empty()){int u=q.front();q.pop();inque[u]=false;for(i=head[u];i!=-1;i=edge[i].next){int v=edge[i].v;int w=edge[i].w;int f=edge[i].f;int t=dis[u]+w;if(f>0&&dis[v]>t){dis[v]=t;pre[v]=i;if(!inque[v]){inque[v]=true;q.push(v);}}}}if(pre[t]==-1) return 0;return 1;
}
void EK(){int flow;while(spfa()){flow=inf;int x=pre[t];while(x!=-1){flow=min(flow,edge[x].f);x=pre[edge[x].u];}x=pre[t];while(x!=-1){edge[x].f-=flow;edge[x^1].f+=flow;ans+=edge[x].w*flow;x=pre[edge[x].u];}maxflow+=flow;}
}
int main(){cin>>n>>m;cnt=0;memset(head,-1,sizeof(head));s=1;t=2*n;for(int i=1;i<=m;i++){int a,b,c;cin>>a>>b>>c;if(a==1&&b==n){add(a+n,b,c,1);}else{add(a+n,b,c,inf);}}add(1,1+n,0,inf);add(n,n+n,0,inf);for(int i=2;i<=n-1;i++){add(i,i+n,0,1);}EK();cout<<maxflow<<' '<<ans<<endl;return 0;
}
题目:
志愿者招募
吃豆豆
https://www.wjyyy.top/754.html
航空路线问题
晨跑
餐巾计划
网络扩容
XSY4188