网络流

写一篇网络流费用流的总结吧。
最大流板子:dinic()
bfs求深度后dfs找增广,要求只能向深度+1的点转移

bool bfs()
{
	memset(dep,-1,sizeof(dep));
	dep[s]=0;
	queue<int>q;
	q.push(s);
	while(!q.empty())
	{
		int now=q.front();
		q.pop();
		for(int i=h[now];i;i=fr[i])
		{
			if(f[i]&&dep[to[i]]==-1)
			{
				dep[to[i]]=dep[now]+1;
				q.push(to[i]);
			}
		}
	}
	return dep[t]!=-1;
}
int dfs(int x,int r)
{
	if(x==t)return r;
	int ans=0;
	for(int i=h[x];i;i=fr[i])
	{
		if(f[i]&&dep[to[i]]==dep[x]+1)
		{
			int res=dfs(to[i],min(r,f[i]));
			if(res>0)
			{
				//cout<<x<<' '<<to[i]<<endl;
				r-=res;
				f[i]-=res;
				f[i^1]+=res;
				ans+=res;
				if(!r)return ans;
			}
		}
	}
	if(!ans)dep[x]=-1;
	return ans;
}
int dinic()
{
	int flow=0;
	while(bfs())
	{
		flow+=dfs(s,1e9);
	}
	return flow;
}

费用流板子:spfa或者dijk(好像叫什么原始对偶算法)
spfa和dijk都用来找最短(长)路并记录一下路径,这相当于在保证最小费用的情况下找增广。spfa没什么好说的,dijk的话因为有负权边,直接求会产生错误,这时就要加一个数组h,称为势。假设要从x扩展到y,边长本来应为w,在势的影响下,我们重新定义边长为h[x]-h[y]+w,这就要求我们的h数组满足规律h[x]-h[y]+w>=0,我们发现我们求的dis数组刚好也有这个规律,因此我们把上次扩展完后得到的dis数组作为本次的h数组,非常优美。
用spfa和dijk本质上一样,不过在找最短路方面dijk肯定更稳定,不过一般的题数据规模小,没什么必要。

bool spfa()
{
	memset(dis,0x80,sizeof(dis));
	dis[s]=0;
	queue<int>q;
	q.push(s);
	while(!q.empty())
	{
		int now=q.front();
		q.pop();
		vis[now]=0;
		for(int i=h[now];i;i=fr[i])
		{
			if(f[i]&&dis[to[i]]<dis[now]+w[i])
			{
				dis[to[i]]=dis[now]+w[i];
				pre[to[i]]=i;
				prn[to[i]]=now;
				if(!vis[to[i]])
				{
					vis[to[i]]=1;
					q.push(to[i]);
				}
			}
		}
	}
	return dis[t]!=dis[3999];
}
int mfmc()
{
	int ans=0;
	while(spfa())
	{
		int x=t,res=1e9;
		while(x!=s)
		{
			res=min(res,f[pre[x]]);
			x=prn[x];
		}
		x=t;
		while(x!=s)
		{
			f[pre[x]]-=res;
			f[pre[x]^1]+=res;
			x=prn[x];
		}
		//cout<<"fuck";
		ans+=res*dis[t];
	}
	return ans;
}

一些常见套路:
1.拆点,很多时候我们要限制一个点的使用次数,这个时候可以把,每个点拆成入点和出点,中间连一条流量为这个点可用次数的边。还有一种毒瘤情况就是每个点在路径上和路径结尾效果不同,这是要拆成三个点,入点原点出点顺次连接,原点和源点汇点相连,这样一个点在路径中间和两端的效果就不同了。比如这个题
有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,最终达到目标状态。要求第i行第j列的格子只能参与mi,j次交换。
我们首先把白点看成没有,那么只要把黑点放到对的地方即可。我们发现,每个由白变黑的点最后从四周的点流来了1,向t流了1,(当然还有途径他的一些流量,这部分是平衡的),同理又黑变白的点把s流来的1流出去了。这样子,我们可以把每个点拆成两个,让每个点被经过的次数为他的流量,费用为1。但是这样有一个问题,就是在一个黑点的移动过程中,路程中的点实际被经过了两次,而两头的点仅被经过一次,为了体现这个差别,我们就要像上面说的那样拆成三个点,入点和原点间、原点和出点间流量为原流量一半。

2.输出方案。输出路径的话dfs残留网络,如果反边有剩余流量就表示要走这条边。求最小割的方案其实是看最后一次bfs时那些点和源点联通,哪些点和汇点联通。
3.黑白染色,常见于棋盘上,把点分为内部不相互影响的两个集合,比如方格取数。分成两个集合后就可以连边跑一些最小割之类的东西。这个东西其实叫什么最大权闭合子图问题,也就是当我们要舍弃一些价值时,建立一个有依赖关系的图(不一定是二分图),跑最小割,那么割掉的那些边显然就是我们拿了不划算的。
题目
这个嘛,观察之后黑点放东西影响的都是白点,所以把超级源点连向每个黑点,每个白点连向超级汇点,每个黑点连向影响的白点,这样跑了最小割后,就相当于我们把一些不值得拿的位置割掉了。最后用总的能放棋子的地方数减去最小割,就是答案。
4.按时间拆点,在每个时间点上都把每个节点建一个复制,然后从前一秒可转移过来的位置转移过来。比如这个题
按照时间拆点后可以保证图的层次,其实和一般的拆点思路差不多。
这个题我们显然不能直接搞,所以在每个时间点都把地球,月球,空间站建一遍,然后从小到大枚举答案,每次从相应的飞船的上一个位置连向这一秒的位置,容量为飞船容量,并且每个点都从他上一秒的对应位置连过来,容量inf,相当于空间站容量无限。这个图建起来就长这样
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值