网络流
网络流通常有一个源点,一个汇点,和其他若干点,这些点之间有若干条有向边,每条边上有一个负载上限,称为容量。题目通常要求我们求出在所有边容量允许的范围内,从源点到汇点流量的最大值。
一般情况下,网络流都不会很难写,网络流题目大多数都难在建图上。
dinic算法:
step1:
bfs给所有有可行流的点标号,建立层次网络,源点标为1,将所有能标的点标完或标到汇点结束;
step2:
如果汇点被标记,dfs找到所有可行流并修改路径上的容量;
如果汇点没有被标记,结束算法。
注意事项:
1、每次bfs时每次标记数组要清空;
2、bfs在拓展状态时不仅要看是否被标记过,还要看是否有可行流;
3、bfs可以全部标记,也可以标记到汇点就结束;
4、dfs回溯时,反向边要加上流量;
5、dfs结束时,可以判断是否有通过该点的流量,如果没有,可以把该点从层次网络中清除,进行优化。
模板代码如下:
bool bfs()
{
static int dl[N];
memset(d,0,sizeof(d));
int l=1,r=1;
dl[1]=s;d[s]=1;
while(l<=r)
{
int c=dl[l++];
for(int t=fir[c];t;t=nes[t])
{
if(d[v[t]] || !q[t]) continue;
dl[++r]=v[t];
d[v[t]]=d[c]+1;
if(v[t]==e) return true;
}
}
return false;
}
int dfs(int c,int flow)
{
if(c==e || flow==0) return flow;
int ans=0;
for(int t=fir[c];t;t=nes[t])
{
if(d[v[t]]!=d[c]+1 || !q[t]) continue;
int f=dfs(v[t],min(flow,q[t]));
ans+=f;flow-=f;
q[t]-=f; q[t^1]+=f;
if(flow==0) break;
}
if(!ans) d[c]=-1;
return ans;
}
int dinic()
{
int ans=0;
while(bfs()) ans+=dfs(s,lar);
return ans;
}
【Usaco2007 Open】bzoj1711 Dining吃饭
题目大意:
有n头牛,F种食物,G种饮料
每头牛分别喜欢一些食物和一些饮料
每种饮料和食物都只有一个
问最多能让几头牛吃到自己喜欢的食物并喝到自己喜欢的饮料
题目分析:
从源点向每种食物连一条容量为1的边,代表每种食物只有一个;
从每种饮料向汇点连一条容量为1的边,代表每种饮料只有一个;
然后用两个点代表一头牛,中间连一条容量为1的边,代表每头牛只选择一套食物和饮料;
把第一个代表牛的点和喜欢的食物连一条容量为1的边,把第二个代表牛的点和喜欢的饮料连一条容量为1的边;
然后跑最大流就可以了。
注意事项:
1、在写邻接表存边的时候要写接口(不写接口会死人的呀QAQ,多么痛的领悟);
2、在给所有的点编号的时候要小心不要把两个点的编号编重;
3、背好模板。
主函数代码如下:
void edge(int x,int y,int z)
{
top++;
v[top]=y;q[top]=z;
nes[top]=fir[x];fir[x]=top;
}
int main()
{
scanf("%d%d%d",&n,&F,&D);
for(int i=1;i<=n;i++) edge(i,i+100,1,++top),edge(i+100,i,0,++top);
for(int i=1;i<=F;i++) edge(s,200+i,1,++top),edge(200+i,s,0,++top);
for(int i=1;i<=D;i++) edge(300+i,e,1,++top),edge(e,300+i,0,++top);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&fi,&di);
for(int j=1;j<=fi;j++)
{
scanf("%d",&x);
edge(200+x,i,1,++top);
edge(i,200+x,0,++top);
}
for(int j=1;j<=di;j++)
{
scanf("%d",&x);
edge(i+100,300+x,1,++top);
edge(300+x,i+100,0,++top);
}
}
printf("%d",dinic());
return 0;
}