一,无源网络流的建模
https://www.luogu.org/problemnew/show/P1231
题意,给你n1本书,n2本练习册,n3本答案,给你这些书和答案对应关系,问你最多能组成多少本书册。
由于需要书,练习册,答案三件套才能组成完整书册,将书复制成两份,一份与练习册建立边,一份与答案建立边,
建立源点和汇点,跑一边最大流就能解决
#include<bits/stdc++.h>
using namespace std;
const int maxn=100000;
const int INF=0x3fffffff;
struct Dinic
{
struct Edge
{
int from,to,cap,flow;
};
int n,m,s,t;
vector<Edge>edges;
vector<int>G[maxn];
bool vis[maxn];
int d[maxn];
int cur[maxn];
void AddEdge(int from,int to,int cap)
{
edges.push_back((Edge)
{
from,to,cap,0
});
edges.push_back((Edge)
{
to,from,0,0
});//反向弧
m=edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool BFS()
{
memset(vis,0,sizeof(vis));
queue<int>Q;
Q.push(s);
d[s]=0;
vis[s]=1;
while(!Q.empty())
{
int x=Q.front();
Q.pop();
for (int i=0; i<G[x].size() ; i++)
{
Edge& e=edges[G[x][i]];
if(!vis[e.to]&&e.cap>e.flow)
{
vis[e.to]=1;
d[e.to]=d[x]+1;
Q.push(e.to);
}
}
}
return vis[t];
}
int DFS(int x,int a)//x为当前节点,a为目前为止所有弧的最小流量
{
if(x==t||a==0)return a;
int flow=0,f;
for (int& i=cur[x]; i<G[x].size() ; i++ )
{
Edge& e = edges[G[x][i]];
if(d[x]+1==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0)//下一层次加1并且残留量不为零
{
e.flow+=f;
edges[G[x][i]^1].flow-=f;
flow+=f;
a-=f;
if(a==0)break;
}
}
return flow;
}
int Maxflow(int s,int t)
{
this->s=s;
this->t=t;
int flow=0;
while(BFS())
{
memset(cur,0,sizeof(cur));
flow+=DFS(s,INF);
}
return flow;
}
};
Dinic dc;
int main()
{
int n1,n2,n3,m1,m2,m3;
scanf("%d %d %d",&n1,&n2,&n3);
scanf("%d",&m1);
for(int i=1; i<=m1; i++)//练习册指书的边
{
int x,y;
scanf("%d %d",&x,&y);
dc.AddEdge(y,x+n2,1);
}
scanf("%d",&m2);
for(int i=1; i<=m2; i++)//书指向答案的边
{
int x,y;
scanf("%d %d",&x,&y);
dc.AddEdge(x+n2+n1,2*n1+n2+y,1);
}
for(int i=1; i<=n2; i++)//建立源点指向练习册的边
{
dc.AddEdge(0,i,1);
}
for(int i=1; i<=n1; i++)//建立书指向书的边
{
dc.AddEdge(n2+i,n2+n1+i,1);
}
int end=n1*2+n2+n3+1;
for(int i=1; i<=n3; i++)//建立书指向终点的边
{
dc.AddEdge(n2+2*n1+i,end,1);
}
printf("%d",dc.Maxflow(0,end));
return 0;
}
二,建立二分图跑网络流
题意,给你个二分图,每次选择一条边会给每个点加一个度,问能否使得点的度的范围在[l,r]之间。
https://nanti.jisuanke.com/t/31447
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=20010;
const int N=0x3f3f3f3f;
const int INF=0x3fffffff;
int degree[maxn];
int u[maxn];
int v[maxn];
struct Dinic
{
struct Edge
{
int from,to,cap,flow;
};
int n,m,s,t;
vector<Edge>edges;
vector<int>G[maxn];
bool vis[maxn];
int d[maxn];
int cur[maxn];
void AddEdge(int from,int to,int cap)
{
edges.push_back((Edge)
{
from,to,cap,0
});
edges.push_back((Edge)
{
to,from,0,0
});//反向弧
m=edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool BFS()
{
memset(vis,0,sizeof(vis));
queue<int>Q;
Q.push(s);
d[s]=0;
vis[s]=1;
while(!Q.empty())
{
int x=Q.front();
Q.pop();
for (int i=0; i<G[x].size() ; i++)
{
Edge& e=edges[G[x][i]];
if(!vis[e.to]&&e.cap>e.flow)
{
vis[e.to]=1;
d[e.to]=d[x]+1;
Q.push(e.to);
}
}
}
return vis[t];
}
int DFS(int x,int a)//x为当前节点,a为目前为止所有弧的最小流量
{
if(x==t||a==0)return a;
int flow=0,f;
for (int& i=cur[x]; i<G[x].size() ; i++ )
{
Edge& e = edges[G[x][i]];
if(d[x]+1==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0)//下一层次加1并且残留量不为零
{
e.flow+=f;
edges[G[x][i]^1].flow-=f;
flow+=f;
a-=f;
if(a==0)break;
}
}
return flow;
}
int Maxflow(int s,int t)
{
this->s=s;
this->t=t;
int flow=0;
while(BFS())
{
memset(cur,0,sizeof(cur));
flow+=DFS(s,INF);
}
return flow;
}
};
void init()
{
memset(degree,0,sizeof(degree));
return;
}
int main()
{
int cas,i,l,r,minn,maxx,res,sum,n,m,k;
cas=1;
while(scanf("%d%d%d",&n,&m,&k)!=EOF)
{
init();
scanf("%d%d",&l,&r);
for(i=1; i<=k; i++)
{
scanf("%d%d",&u[i],&v[i]);
v[i]+=n;
degree[u[i]]++,degree[v[i]]++;
}
minn=N,maxx=0;
for(i=1; i<=n+m; i++)
{
minn=min(minn,degree[i]);
maxx=max(maxx,degree[i]);
}
if(l<=minn)
{
if(maxx<=r) printf("Case %d: Yes\n",cas++);
else
{
for(i=1; i<=k; i++)
{
if(degree[u[i]]>r&°ree[v[i]]>r)
{
degree[u[i]]--,degree[v[i]]--;
}
}
int source=0,sink=n+m+1;
Dinic dc;
for(i=1; i<=k; i++)
{
if(degree[u[i]]>r&°ree[v[i]]>l) dc.AddEdge(u[i],v[i],1);
else if(degree[v[i]]>r&°ree[u[i]]>l) dc.AddEdge(v[i],u[i],1);
}
sum=0;
for(i=1; i<=n+m; i++)
{
if(degree[i]>r)
{
dc.AddEdge(source,i,degree[i]-r);
sum+=(degree[i]-r);
}
else if(degree[i]>l)
{
dc.AddEdge(i,sink,degree[i]-l);
}
}
res=dc.Maxflow(source,sink);
if(res==sum) printf("Case %d: Yes\n",cas++);
else printf("Case %d: No\n",cas++);
}
}
else printf("Case %d: No\n",cas++);
}
return 0;
}
三,无源上下界网络流问题
模板题
题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1314
#include<bits/stdc++.h>
using namespace std;
const int maxn=100005;
const int INF=0x3fffffff;
struct Dinic
{
struct Edge
{
int from,to,cap,flow;
};
int n,m,s,t;
vector<Edge>edges;
vector<int>G[maxn];
bool vis[maxn];
int d[maxn];
int cur[maxn];
void init(int n)
{
for (int i=0; i<=n+1 ; i++ )
G[i].clear();
edges.clear();
}
void AddEdge(int from,int to,int cap)
{
edges.push_back((Edge)
{
from,to,cap,0
});
edges.push_back((Edge)
{
to,from,0,0
});//反向弧
m=edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool BFS()
{
memset(vis,0,sizeof(vis));
queue<int>Q;
Q.push(s);
d[s]=0;
vis[s]=1;
while(!Q.empty())
{
int x=Q.front();
Q.pop();
for (int i=0; i<G[x].size() ; i++)
{
Edge& e=edges[G[x][i]];
if(!vis[e.to]&&e.cap>e.flow)
{
vis[e.to]=1;
d[e.to]=d[x]+1;
Q.push(e.to);
}
}
}
return vis[t];
}
int DFS(int x,int a)//x为当前节点,a为目前为止所有弧的最小流量
{
if(x==t||a==0)return a;
int flow=0,f;
for (int& i=cur[x]; i<G[x].size() ; i++ )
{
Edge& e = edges[G[x][i]];
if(d[x]+1==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0)//下一层次加1并且残留量不为零
{
e.flow+=f;
edges[G[x][i]^1].flow-=f;
flow+=f;
a-=f;
if(a==0)break;
}
}
return flow;
}
int Maxflow(int s,int t)
{
this->s=s;
this->t=t;
int flow=0;
while(BFS())
{
memset(cur,0,sizeof(cur));
flow+=DFS(s,INF);
}
return flow;
}
};
int d[maxn];
int l[maxn];
Dinic dc;
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,m;
scanf("%d%d",&n,&m);
dc.init(n);
memset(d,0,sizeof d);
for (int i=0; i<m ; i++ )
{
int u,v,low,up;
scanf("%d%d%d%d",&u,&v,&low,&up);
d[u]+=low;
d[v]-=low;
l[i]=low;
dc.AddEdge(u,v,up-low);
}
int ss=0;
for (int i=1; i<=n ; i++ )
{
if(d[i]>0)
{
dc.AddEdge(i,n+1,d[i]);
ss+=d[i];
}
else
dc.AddEdge(0,i,-d[i]);
}
if(dc.Maxflow(0,n+1)==ss)
{
cout<<"YES"<<endl;
for (int i=0; i<m ; i++ )
{
cout<<dc.edges[2*i].flow+l[i]<<endl;
}
}
else
cout<<"NO"<<endl;
}
return 0;
}
四,二分图,建立源点的有源上下界网络流
题目和上面的二分图一样,解决方法是建立一个源点连向左图,每条边的范围要是[l,r],右图连向汇点,范围一样,左图与右图的边的范围是[0,1],就转化为上面的模板题了。
#include<bits/stdc++.h>
using namespace std;
const int maxn=100005;
const int INF=0x3fffffff;
struct Dinic
{
struct Edge
{
int from,to,cap,flow;
};
int n,m,s,t;
vector<Edge>edges;
vector<int>G[maxn];
bool vis[maxn];
int d[maxn];
int cur[maxn];
void init(int n)
{
for (int i=0; i<=maxn ; i++ )
G[i].clear();
edges.clear();
}
void AddEdge(int from,int to,int cap)
{
edges.push_back((Edge)
{
from,to,cap,0
});
edges.push_back((Edge)
{
to,from,0,0
});//反向弧
m=edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool BFS()
{
memset(vis,0,sizeof(vis));
queue<int>Q;
Q.push(s);
d[s]=0;
vis[s]=1;
while(!Q.empty())
{
int x=Q.front();
Q.pop();
for (int i=0; i<G[x].size() ; i++)
{
Edge& e=edges[G[x][i]];
if(!vis[e.to]&&e.cap>e.flow)
{
vis[e.to]=1;
d[e.to]=d[x]+1;
Q.push(e.to);
}
}
}
return vis[t];
}
int DFS(int x,int a)//x为当前节点,a为目前为止所有弧的最小流量
{
if(x==t||a==0)return a;
int flow=0,f;
for (int& i=cur[x]; i<G[x].size() ; i++ )
{
Edge& e = edges[G[x][i]];
if(d[x]+1==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0)//下一层次加1并且残留量不为零
{
e.flow+=f;
edges[G[x][i]^1].flow-=f;
flow+=f;
a-=f;
if(a==0)break;
}
}
return flow;
}
int Maxflow(int s,int t)
{
this->s=s;
this->t=t;
int flow=0;
while(BFS())
{
memset(cur,0,sizeof(cur));
flow+=DFS(s,INF);
}
return flow;
}
};
int d[maxn];
int l[maxn];
Dinic dc;
int main()
{
int n,m,k;
int acm=1;
while(~scanf("%d%d%d",&n,&m,&k))
{
dc.init(n);
int l,r;
scanf("%d%d",&l,&r);
memset(d,0,sizeof d);
for(int i=1; i<=n; i++)
{
dc.AddEdge(0,n,r-l);
d[0]+=l;
d[i]-=l;
}
for(int i=1; i<=k; i++)
{
int u,v;
scanf("%d%d",&u,&v);
dc.AddEdge(u,v+n,1);
}
for(int i=n+1; i<=n+m; i++)
{
dc.AddEdge(i,n+m+1,r-l);
d[i]+=l;
d[n+m+1]-=l;
}
int ss=0;
for (int i=0; i<=m+n+1 ; i++ )
{
if(d[i]>0)
{
dc.AddEdge(i,n+m+3,d[i]);
ss+=d[i];
}
else
dc.AddEdge(n+m+2,i,-d[i]);
}
dc.AddEdge(n+m+1,0,0x3fffffff);
if(dc.Maxflow(n+m+2,n+m+3)==ss)
{
printf("Case %d: Yes\n",acm++);
}
else
printf("Case %d: No\n",acm++);
}
return 0;
}
五,https://hihocoder.com/problemset/problem/1398?sid=1288265#
最大权闭合子图
#include<bits/stdc++.h>
using namespace std;
const int maxn=1005;
const int INF=0x3fffffff;
struct Dinic
{
struct Edge
{
int from,to,cap,flow;
};
int n,m,s,t;
vector<Edge>edges;
vector<int>G[maxn];
bool vis[maxn];
int d[maxn];
int cur[maxn];
void AddEdge(int from,int to,int cap)
{
edges.push_back((Edge)
{
from,to,cap,0
});
edges.push_back((Edge)
{
to,from,0,0
});//反向弧
m=edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool BFS()
{
memset(vis,0,sizeof(vis));
queue<int>Q;
Q.push(s);
d[s]=0;
vis[s]=1;
while(!Q.empty())
{
int x=Q.front();
Q.pop();
for (int i=0; i<G[x].size() ; i++)
{
Edge& e=edges[G[x][i]];
if(!vis[e.to]&&e.cap>e.flow)
{
vis[e.to]=1;
d[e.to]=d[x]+1;
Q.push(e.to);
}
}
}
return vis[t];
}
int DFS(int x,int a)//x为当前节点,a为目前为止所有弧的最小流量
{
if(x==t||a==0)return a;
int flow=0,f;
for (int& i=cur[x]; i<G[x].size() ; i++ )
{
Edge& e = edges[G[x][i]];
if(d[x]+1==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0)//下一层次加1并且残留量不为零
{
e.flow+=f;
edges[G[x][i]^1].flow-=f;
flow+=f;
a-=f;
if(a==0)break;
}
}
return flow;
}
int Maxflow(int s,int t)
{
this->s=s;
this->t=t;
int flow=0;
while(BFS())
{
memset(cur,0,sizeof(cur));
flow+=DFS(s,INF);
}
return flow;
}
};
Dinic dc;
int main()
{
int n,m,u,v,w;
scanf("%d%d",&n,&m);
for (int i=0; i<m ; i++ )
{
scanf("%d",&w);
dc.AddEdge(n+i+1,n+m+1,w);
}
int sum=0;
for (int i=0; i<n ; i++ )
{
scanf("%d",&w);
sum+=w;
dc.AddEdge(0,i+1,w);
int t;
scanf("%d",&t);
for (int j=0;j<t ;j++ )
{
scanf("%d",&v);
dc.AddEdge(i+1,n+v,INF);
}
}
printf("%d",sum-dc.Maxflow(0,n+m+1));
return 0;
}