网络流模板 https://www.luogu.org/problemnew/show/P3376
大概思路就是每次都bfs搞一条路径,然后记录这条路径,对他进行增广,反边+minv(方便于下次走回来),正边-minv
(minv就是一条边上边权最小的值)
注意bfs的时候若w[i]==0这条边是走不了的!
代码在这里
#include <bits/stdc++.h>
using namespace std;
const int maxn=200005;
const int INF=0x3f3f3f3f;
int u[maxn],v[maxn],w[maxn],nxt[maxn],head[maxn];
int inque[maxn],fa[maxn];
int cnt=-1;int n,m,s,t;
queue<int> q;
void add_edge(int x,int y,int z)
{
cnt++;u[cnt]=x;v[cnt]=y;w[cnt]=z;
nxt[cnt]=head[x];head[x]=cnt;
}
int bfs(int s)
{
memset(inque,0,sizeof(inque));while(!q.empty()) q.pop();
q.push(s);inque[s]=1;
while(!q.empty())
{
int x=q.front();q.pop();
for(int i=head[x];i!=-1;i=nxt[i])
{
if(w[i]==0) continue;
if(!inque[v[i]])
{
fa[v[i]]=i;
q.push(v[i]);inque[v[i]]=1;
}
if(v[i]==t) return 1;
}
}
return 0;
}
int zg()
{
int minv=INF;
for(int i=t;i!=s;i=u[fa[i]])
{
minv=min(minv,w[fa[i]]);
}
for(int i=t;i!=s;i=u[fa[i]])
{
w[fa[i]]-=minv;w[fa[i]^1]+=minv;
}
return minv;
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d%d%d%d",&n,&m,&s,&t);
for(int i=1;i<=m;i++)
{
int x,y,z;scanf("%d%d%d",&x,&y,&z);
add_edge(x,y,z);add_edge(y,x,0);
}
int ans=0;
while(bfs(s))
{
ans+=zg();
}
printf("%d",ans);
return 0;
}
酒店之王 https://www.luogu.org/problemnew/show/P1402
这个题真想不到是网络流啊
简单总结一下:弄个大源点和大汇点,源点连向所有的菜,菜连向人,人一定要自己联向自己,(不然一个人就可以走两次惹)然后自己连向房间,最后所有的房间连向大汇点。
最后所有的边权都是1emmmm。
#include <bits/stdc++.h>
using namespace std;
const int maxn=200005;
int u[maxn],v[maxn],w[maxn];int inque[maxn];
int head[maxn],nxt[maxn];int cnt=-1;
int fa[maxn];int s,t;
void add_edge(int x,int y,int z)
{
cnt++;u[cnt]=x;v[cnt]=y;w[cnt]=z;
nxt[cnt]=head[x];head[x]=cnt;
}
int bfs(int s)
{
memset(inque,0,sizeof(inque));
queue<int> q;
q.push(s);inque[s]=1;
while(!q.empty())
{
int x=q.front();q.pop();
for(int i=head[x];i!=-1;i=nxt[i])
{
if(w[i]==0) continue;
if(!inque[v[i]])
{
q.push(v[i]);inque[v[i]]=1;
fa[v[i]]=i;
}
if(v[i]==t) return 1;
}
}
return 0;
}
int zg()
{
// cout<<"here"<<endl;
int minv=0x3f3f3f3f;
for(int i=t;i!=s;i=u[fa[i]])
{
minv=min(minv,w[fa[i]]);
}
for(int i=t;i!=s;i=u[fa[i]])
{
w[fa[i]]-=minv;w[fa[i]^1]+=minv;
}
return minv;
}
int main()
{
memset(head,-1,sizeof(head));
int n,p,q;scanf("%d%d%d",&n,&p,&q);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=p;j++)
{
int x;cin>>x;if(x==0) continue;
add_edge(j+n,i,1);add_edge(i,j+n,0);
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=q;j++)
{
int x;cin>>x;if(x==0) continue;
add_edge(i+p+n+q,j+p+n,1);add_edge(j+p+n,i+p+n+q,0);
}
}
for(int i=1;i<=n;i++) {add_edge(i,n+p+q+i,1);add_edge(n+p+q+i,i,0);}
s=0,t=n+p+q+n+1;
for(int i=1;i<=p;i++) {add_edge(s,i+n,1);add_edge(i+n,s,0);}
for(int i=1;i<=q;i++) {add_edge(i+n+p,t,1);add_edge(t,i+n+p,0);}
int ans=0;
while(bfs(s))
{
ans+=zg();
}
printf("%d",ans);
return 0;
}
圆桌聚餐 https://loj.ac/problem/6004
和酒店之王差不多,都是要自己建模的
同样的我们也要做一个大源点和大汇点。
分四层:源点,单位,桌子,汇点
因为一个单位的人一张桌子上最多只能有一个人,所以我们将单位与桌子相连,设权值为1。然后很显然的,源点连向单位的是c[i],桌子连向汇点的就是r[i]。
then跑一下网络流就可以了。
费用最大流 https://www.luogu.org/problemnew/show/P3381
bfs改成spfa,更新的时候记录一下边。这样可以保证每次找到的边都是最短路。
然后注意最后要乘上dis[t]就可以了。
#include <bits/stdc++.h>
using namespace std;
const int maxn=50005;
const int Inf=0x3f3f3f3f;
int n,m,s,t,cnt=-1,ans1=0;
int u[maxn],v[maxn],w[maxn],nxt[maxn],head[maxn];
int inque[maxn],dis[maxn],c[maxn],fa[maxn];
void add_edge(int x,int y,int z,int f)
{
cnt++;u[cnt]=x;v[cnt]=y;w[cnt]=z;c[cnt]=f;
nxt[cnt]=head[x];head[x]=cnt;
}
bool bfs(int s)
{
memset(dis,Inf,sizeof(dis));memset(inque,0,sizeof(inque));
queue<int> q;q.push(s);inque[s]=1;dis[s]=0;
while(!q.empty())
{
int x=q.front();q.pop();inque[x]=0;
for(int i=head[x];i!=-1;i=nxt[i])
{
if(w[i]==0) continue;
if(dis[v[i]]>dis[x]+c[i])
{
dis[v[i]]=dis[x]+c[i];
fa[v[i]]=i;
if(!inque[v[i]])
{
q.push(v[i]);
inque[v[i]]=1;
}
}
}
}
return dis[t]!=Inf;
}
int zg()
{
int minv=Inf;
for(int i=t;i!=s;i=u[fa[i]])
{
minv=min(minv,w[fa[i]]);
}
for(int i=t;i!=s;i=u[fa[i]])
{
w[fa[i]]-=minv;w[fa[i]^1]+=minv;
}
ans1+=minv;
return minv*dis[t];
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d%d%d%d",&n,&m,&s,&t);
for(int i=1;i<=m;i++)
{
int x,y,z,f;scanf("%d%d%d%d",&x,&y,&z,&f);
add_edge(x,y,z,f);add_edge(y,x,0,-f);
}
int ans=0;
while(bfs(s))
{
ans+=zg();
}
printf("%d %d",ans1,ans);
return 0;
}