网络流的几个小优化
是不是觉得网络流的速度不尽人心?
优化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;
}