网络流之dinic算法

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]的和
 */


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值