网络流的几个小优化

是不是觉得网络流的速度不尽人心?

优化1

优化1就是我们空间的优化,有时候我们打.other是不是打得心烦气燥?

我们可以在初始化时len=1,那么以后要访问k的other时只需要k^1就可以了,简单来说就是二进制最后一位由1变0,由0变1。

同时,有时候一条边的x也不大有用,所以我们一样可以省去,所以,这两个变量的省去让我们代码简洁易懂,又省空间,一举两得!


优化2(弧优化)

好了,当前弧优化是什么呢(虽然有些大佬自己推出了,但自己可能也不知道自己加了优化!)?

根据最大流或zkw费用流(不会的可以点开我的博客,我已经置顶了),每次搜过的边基本上都是流不了的了,如果再去搜会十分浪费。

那么,我们可以用一个数组保存每个点的搜到第几条边,每次用k慢慢跳到那跳边,再开始搜,同时,最后,再从第一条边开始搜到没搜的那条边的前一条,可以缩短一些时间!

但是,每次慢慢跳太慢,可以直接记录是哪条边,嘿嘿嘿,就不用前面慢慢找了!

当然,我给出最短的当前弧(最开始我打的那种长,但是稍稍快1ms左右QAQ,其实也差不了多少,还不如最短的好)。

zkw流(只含递归部分,其他照旧,顺便说一下,MCMF加不了这个优化!):

#include<cstdio>
#include<cstring>
#include<algorithm>
using  namespace  std;
typedef  long  long  ll;
struct  node
{
    int  y,next;
    ll  c,k;
}a[201000];int  last[1000],len=1;
long  long  d[1100];
bool  v[1100];
int  n,m,st,ed;
ll  cost=0;
int  cur[1100];
inline void  ins(int  x,int  y,ll  c,ll  k)
{
    len++;
    a[len].y=y;a[len].c=c;a[len].k=k;
    a[len].next=last[x];last[x]=len;
    len++;
    a[len].y=x;a[len].c=0;a[len].k=-k;
    a[len].next=last[y];last[y]=len;
}
int  list[1100],head,tail;/*队列*/
inline  bool  spfa()
{
    memset(v,false,sizeof(v));v[ed]=true;/*判断是否进入队列*/
    memset(d,10,sizeof(d));d[ed]=0;/*从终点到这里要多少费用*/
    head=1;tail=2;list[head]=ed;/*从终点出发*/
    ll  inf=d[st];
    while(head!=tail)
    {
        int  x=list[head];
        for(int  k=last[x];k;k=a[k].next)
        {
			int  y=a[k].y,kl=k^1;
            if(a[kl].c>0/*由于是倒着搜的,所以边也要反向边*/  &&  a[kl].k+d[x]<d[y])/*判断边是否可行并更新*/
            {
                d[y]=a[kl].k+d[x];/*更新*/
                if(v[y]==false)
                {
                    v[y]=true;
					list[tail++]=y;if(tail==n+1)tail=1;
                }
            }
        }
        head++;
        if(head==n+1)head=1;
        v[x]=false;
    }
    return  d[st]!=inf;/*返回bool值*/
}
inline  ll  mymin(ll  x,ll  y){return  x<y?x:y;}/*找最小值*/
long  long  find(int  x,ll  f)
{
    if(x==ed)return  f;
    v[x]=true;
    ll  ans=0,t=0;
    for(int  k=cur[x];k;k=a[k].next)
    {
        int  y=a[k].y;
        if(v[y]==false/*这个点没走过才可以走,否则更新边的流量是会Balabala*/  &&  a[k].c>0  &&  d[x]-a[k].k==d[y])
        {
            ans+=t=find(y,mymin(a[k].c,f-ans));/*是不是很眼熟?*/
            a[k].c-=t;a[k^1].c+=t;cost+=t*a[k].k;
			cur[x]=k;//这个很重要,cur[x]要在if里面更新!否则会拖延时间 
			if(ans==f){v[x]=false;return  ans;}
        }
	}
    v[x]=false;
    return  ans;/*妥妥的像最大流*/
}
int  main()
{
    scanf("%d%d",&n,&m);
    st=1;ed=n;
    for(int  i=1;i<=m;i++)
    {
        int  x,y;
        ll  z,l;
        scanf("%d%d%lld%lld",&x,&y,&z,&l);
        ins(x,y,z,l);
    }
    ll  zans=0;
    while(spfa()==true)/*建图完成!*/
    {
    	memcpy(cur+1,last+1,(n<<2));//部分拷贝 
        zans+=find(st,ll(999999999999999));/*多次查找,找出所有增光路哦*/
    }
    printf("%lld %lld",zans,cost);
    return  0;
}

最大流(Dinic):

#include<cstdio>
#include<cstring>
using  namespace  std;
struct  node
{
	int  y,c,next;
}a[210000];int  last[11000],n,m,len=1/*用异或代替.other*/,st,ed;
int  cur[11000];//当前弧 
inline  void  ins(int  x,int  y,int  c)
{
	len++;a[len].y=y;a[len].c=c;a[len].next=last[x];last[x]=len;
	len++;a[len].y=x;a[len].c=0;a[len].next=last[y];last[y]=len;
}
int  h[11000],list[11000],head=1,tail=n;
inline  bool  bt_()
{
	memset(h,0,sizeof(h));h[st]=1;
	head=1;tail=2;list[1]=st;
	while(head!=tail)
	{
		int  x=list[head];cur[x]=last[x];//别忘了初始化cur 
		for(int  k=last[x];k;k=a[k].next)
		{
			int  y=a[k].y;
			if(a[k].c>0  &&  h[y]==0)
			{
				list[tail++]=y;
				h[y]=h[x]+1;
			}
		}
		head++;
	}
	if(h[ed]>0)return  true;
	else  return  false;
}
inline  int  mymin(int  x,int  y){return  x<y?x:y;}
int  find(int  x,int  f)
{
	if(x==ed)return  f;
	int  s=0,t;
	for(int  k=cur[x];k;k=a[k].next)
	{
		int  y=a[k].y;
		if(h[y]==h[x]+1  &&  a[k].c>0)
		{
			s+=(t=find(y,mymin(a[k].c,f-s)));
			a[k].c-=t;a[k^1/*.other*/].c+=t;
			cur[x]=k;//在里面更新很重要 
			if(s==f)return  f;//满足就退出,这步也很重要 
		}
	}
	if(s==0)h[x]=0;
	return  s;
}
int  main()
{
	int  ans=0;
	scanf("%d%d%d%d",&n,&m,&st,&ed);
	for(int  i=1;i<=m;i++)
	{
		int  x,y,c;
		scanf("%d%d%d",&x,&y,&c);
		ins(x,y,c);
	}
	while(bt_()==true)ans+=find(st,999999999);
	printf("%d\n",ans);
	return  0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值