网络流相关

做不出来题所以水博客来了
先来一个Dinic板子,基本来自Yxs学长

include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=505,M=5050;
struct node{
   int from,to,next,w;
}a[2*M];
int head[N],mm=2;
inline void add(int x,int y,int z)
{
   a[mm].from=x;a[mm].to=y;a[mm].w=z;
   a[mm].next=head[x];head[x]=mm++;
}
int n,m,s,t;
int hd[N],d[N];
queue <int> q;
inline bool bfs()
{
   memset(d,0x3f,sizeof(d));
   memcpy(head,hd,sizeof(head));
   while(!q.empty())q.pop();
   q.push(s);d[s]=0;
   while(!q.empty())
   {
      int x=q.front();q.pop();
      for(int i=head[x];i;i=a[i].next)
      {
         if(!a[i].w)continue;
         int y=a[i].to;
         if(d[y]>d[x]+1)d[y]=d[x]+1,q.push(y);
      }
      if(x==t)return 1;
   }
   return 0;
}
int dfs(int x,int flow)
{
   if(x==t)return flow;
   int rest=flow,k;
   for(int i=head[x];i;head[x]=i=a[i].next)
   {
      if(!a[i].w)continue;
      int y=a[i].to;
      if(d[y]==d[x]+1)
      {
        k=dfs(y,min(a[i].w,rest));
        if(!k)d[y]=0;
        else a[i].w-=k,a[i^1].w+=k,rest-=k;
      }
      if(!rest)break;
   }
   return flow-rest;
}
signed main()
{
   cin>>n>>m>>s>>t;
   for(int i=1;i<=m;i++)
   {
      int x,y,z;scanf("%lld%lld%lld",&x,&y,&z);
      add(x,y,z);add(y,x,0);
   }
   memcpy(hd,head,sizeof(hd));
   int ans=0;
   while(bfs())ans+=dfs(s,1e18);
   cout<<ans;
   return 0;

有一个地方是学长特别强调的,就是在dfs中的时候

for(int i=head[x];i;head[x]=i=a[i].next)
   {
      if(!a[i].w)continue;
      int y=a[i].to;
      if(d[y]==d[x]+1)
      {
        k=dfs(y,min(a[i].w,rest));
        if(!k)d[y]=0;
        else a[i].w-=k,a[i^1].w+=k,rest-=k;
      }
      if(!rest)break;
   } 

这个if(!rest)break; 一定要写到循环之内,不能写在循环上面
for(int i=head[x];i&&rest;head[x]=i=a[i].next) 是错误的,会导致特别慢
还有里面这个k=dfs(y,min(a[i].w,rest)); ,要和rest取min,不能和flow取min,否则会导致算出来的最大流变大 我因为这调了一天
时间复杂度是个玄学,理论\(O(n^2m)\),实际根本跑不满,\(n=1e6\)都能1s跑完

暂时没打EK,等费用流再说吧
\(upd\)
做了费用流
spfa费用流就是用spfa代替EK的bfs
贴个板

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=50050;
struct node{
	int from,to,next,w,c;
}a[2*N];
int head[N],mm=2;
inline void add(int x,int y,int z,int c)
{	
	a[mm].from=x;a[mm].to=y;a[mm].w=z;a[mm].c=c;
	a[mm].next=head[x];head[x]=mm++;
}
inline void gan(int x,int y,int z,int c)
{
	add(x,y,z,c);add(y,x,0,-c);
}
int flow[N],pre[N],d[N];
queue<int> q;bool v[N];
int n,m,s,t,ans1,ans2;
inline bool spfa()
{	
	memset(d,0x3f,sizeof(d));
	memset(v,0,sizeof(v));
	d[s]=0;v[s]=1;flow[s]=1e9;
	q.push(s);
	while(q.size())
	{
		int x=q.front();q.pop();v[x]=0;
		for(int i=head[x];i;i=a[i].next)
		{
			if(!a[i].w)continue;
			int y=a[i].to;
			if(d[x]+a[i].c<d[y])
			{
				d[y]=d[x]+a[i].c;
				flow[y]=min(flow[x],a[i].w);
				pre[y]=i;
				if(!v[y])v[y]=1,q.push(y);
			}				
		}
	}
	if(d[t]>=1e9)return 0;
	else return 1;
}
inline void update()
{	
	int x=t;
	ans1+=flow[t];ans2+=d[t]*flow[t];
	while(x!=s)
	{
		int i=pre[x];
		a[i].w-=flow[t];
		a[i^1].w+=flow[t];
		x=a[i].from;	
	}
}
signed main()
{
	cin>>n>>m>>s>>t;
	for(int i=1;i<=m;i++)
	{
		int x,y,z,c;scanf("%lld%lld%lld%lld",&x,&y,&z,&c);
		gan(x,y,z,c);
	}
	while(spfa())update();
	cout<<ans1<<" "<<ans2<<endl;
        return 0;
}

关于建模

1.拆点,对于一些实际问题要把点拆成两个,分别对应流入,流出,方便对其进行限制
2.二分,这部分和网络流经常结合在一起,常常是当求一个最小时间/次数时,二分一个值,然后跑最大流\(check\),每次新建图,记得要清干净
3.对于一一对应的限制问题,建出的大多是二分图,左右各一堆点,之间连边
4.相邻之类限制考虑黑白染色建图,黑白点放在左右

posted @ 2021-08-01 09:54  D'A'T  阅读(51)  评论(0)    收藏  举报