1.关于网络流基本名词和相关问题,参见我博客里的“最大流问题”。
2.解释dinic算法
基本名词:层次。假如从源点到点u经过的最少边数为s(u),那么就称s(u)为u的层次。
在E-K算法(增广路算法)中,我们只使用dfs和bfs中的一种。但是在dinic算法中,dfs和bfs都要用。
运行原理:我们用bfs找出每个节点的层次后,再用dfs找增广路并增广,增广后再bfs....以此类推。在dfs的时候,只能扩展下一层次的点。不难证明,每条增广路都要经过每个层次,所以这样做是没问题的。
3.代码
模板题:poj1273(或者洛谷2740)
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<queue>
#include<map>
#include<climits>
using namespace std;
int n,m,sum=1,s,t;
int h[205],next[405],flow[405],go[405];
int level[205],que[205];
void add(int from,int to,int cost){
sum++;flow[sum]=cost;next[sum]=h[from];h[from]=sum;go[sum]=to;
sum++;flow[sum]=0;next[sum]=h[to];h[to]=sum;go[sum]=from;
}
int dfs(int now,int liu){
int i,from,to,sum=0,kl;
if(now==t)return liu;
for(i=h[now];i!=0;i=next[i])
if(level[go[i]]==level[now]+1&&flow[i]>0){
kl=dfs(go[i],min(liu-sum,flow[i]));
flow[i]-=kl;flow[i^1]+=kl;sum+=kl;
if(liu==sum)return sum;
}
return sum;
}
bool bfs(){
int i,j,head=1,tail=1,from;
memset(level,0,sizeof(level));//注意不要掉了这一句
que[1]=s;level[s]=1;
while(head<=tail){
from=que[head];
if(from==t)return 1;
for(i=h[from];i!=0;i=next[i])
if(level[go[i]]==0&&flow[i]>0){
level[go[i]]=level[from]+1;
tail++;que[tail]=go[i];
}
head++;
}
return 0;
}
int find(){
int ans=0;
while(bfs())ans+=dfs(s,INT_MAX);
return ans;
}
int main()
{
int i,j,x,y,z;
scanf("%d%d",&m,&n);
for(i=1;i<=m;i++){
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
}
s=1;t=n;
printf("%d",find());
return 0;
}
2.例题:SDOI2015星际战争 洛谷3324,bzoj3993
这题是二分+网络流,E-K如果处理不当会被卡,当然加上一些优化也能过
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<map>
#include<climits>
using namespace std;
int n,m;
int s,t,sum=1;
int a[55],b[55];
int h[550],next[100005],go[100005],que[550],level[550];
double flow[100005];
int ma[55][55];
double l=0.0,r,ok,jd=0.0001,mid;
void add(int from,int to,double cost){
sum++;go[sum]=to;flow[sum]=cost*1.0;next[sum]=h[from];h[from]=sum;
sum++;go[sum]=from;flow[sum]=0.0;next[sum]=h[to];h[to]=sum;
}
double dfs(int now,double liu){
int i,j,from;
double ans=0,kl;
if(now==t)return liu;
for(i=h[now];i!=0;i=next[i])
if(level[go[i]]==level[now]+1&&flow[i]>0.0){
if(liu-ans*1.0>flow[i])kl=dfs(go[i],flow[i]);
else kl=dfs(go[i],liu-ans);
ans+=kl;flow[i]-=kl;flow[i^1]+=kl;
if(ans>=liu-jd&&ans<=liu+jd)return ans;
}
return ans;
}
bool bfs(){
int i,j,head=1,tail=1,from;
for(i=1;i<=t;i++)level[i]=0;
que[1]=s;level[s]=1;
while(head<=tail){
from=que[head];
if(from==t)return 1;
for(i=h[from];i!=0;i=next[i])
if(level[go[i]]==0&&flow[i]>0.0){
level[go[i]]=level[from]+1;
tail++;que[tail]=go[i];
}
head++;
}
return 0;
}
bool find(double mid){
int i,j;
double ans=0.0;
sum=1;
for(i=1;i<=t;i++)h[i]=0;
for(i=1;i<=m;i++)add(s,i+n,mid*b[i]*1.0);
for(i=1;i<=n;i++)add(i,t,a[i]*1.0);
for(i=1;i<=m;i++)
for(j=1;j<=ma[i][0];j++)add(i+n,ma[i][j],INT_MAX*1.0);
while(bfs())ans+=dfs(s,INT_MAX*1.0);
if(ans>ok-jd)return 1;
return 0;
}
int main()
{
freopen("war.in","r",stdin);
freopen("war.out","w",stdout);
int i,j,x,cnt=0;
scanf("%d%d",&n,&m);
s=n+m+1;t=n+m+2;
for(i=1;i<=n;i++){
scanf("%d",&a[i]);r=r+a[i];ok=ok+a[i];
}
for(i=1;i<=m;i++){scanf("%d",&b[i]);}
for(i=1;i<=m;i++)
for(j=1;j<=n;j++)
{scanf("%d",&x);
if(x==1){ma[i][0]++;ma[i][ma[i][0]]=j;}}
while(l+jd<=r){
mid=(l+r)/2*1.0;
if(find(mid)){r=mid;}
else l=mid;
}
printf("%.6lf",l);
return 0;
}
/*1~n:机器人
n+1~n+m:武器
n+m+1:源点,n+m+2:汇点
思路:s->武器,flow为ans*b[i]
机器人->t,flow为a[i]
看最大流是不是a[i]的和
*/