• 网络流小结


    注:因为风骨傲天习惯用(Dfs+Dinic),所以不会用到(EK)等其他形式,而预流推进等较高级的,等我学了再说吧……

    一、基础知识

    事实上,这一部分只会包括最大流和最小费用最大流的略解,后面会补上带上下界的。

    • 最大流:(Bfs+Dfs)不多说,反正不难。
    • 最小费用最大流:最短路(+Dfs),和上面差不多,只是改一下(Bfs)即可。
    • 当前弧优化:用(cur)记一下上一次跑哪了即可(上代码,东西都在代码里)。

    最大流:

    inline bool bfs()
    {
    	memset(d,0,sizeof(d));d[s]=1;
    	queue<int> q;q.push(s);
    	while(!q.empty())
    	{
    		int x=q.front();q.pop();
    		for(int i=head[x];i!=-1;i=edge[i].next)
    			if(!d[edge[i].to]&&edge[i].dis)
    				d[edge[i].to]=d[x]+1,q.push(edge[i].to);
    	}
    	return d[t]?1:0;
    }
    inline int dfs(int pos,int dis)
    {
    	if(pos==t) return dis;
    	for(int i=cur[pos];i!=-1;i=edge[i].next)
    	{
    		cur[pos]=i;
    		if(d[edge[i].to]==d[pos]+1&&edge[i].dis>0)
    		{
    			int data=dfs(edge[i].to,min(edge[i].dis,dis));
    			if(data>0)
    			{
    				edge[i].dis-=data;
    				edge[i^1].dis+=data;
    				return data;
    			}
    		}
    	}
    	return 0;
    }
    inline int Dinic()
    {
    	int ans=0;
    	while(bfs())
    	{
    		memcpy(cur,head,sizeof(cur));
    		while(int data=dfs(s,0x3f3f3f3f))
    			ans+=data;
    	}
    	return ans;
    }
    

    费用流:

    inline bool bfs()
    {
        for(int i=1;i<=n;i++) d[i]=-0x3f3f3f3f;
        memset(v,0,sizeof(v));memset(flo,0,sizeof(flo));
        queue<int> q;q.push(s);d[s]=0;v[s]=1;flo[s]=1;
        while(!q.empty())
        {
            int x=q.front(); q.pop(); v[x]=0;
            for(int i=head[x];i!=-1;i=edge[i].next)
            {
                int y=edge[i].to;
                if(edge[i].dis>0&&d[y]<d[x]+edge[i].cos)
                {
                    d[y]=d[x]+edge[i].cos;
                    flo[y]=flo[x]+1;
                    if(!v[y]){q.push(y);v[y]=1;}
                }
            }
        }
        if(d[t]==-0x3f3f3f3f) return 0;
        else return 1;
    }
    int dfs(int pos,int dis)
    {
        if(pos==t) return dis;
        for(int i=cur[pos];i!=-1;i=edge[i].next)
        {
            int y=edge[i].to;cur[pos]=i;
            if(flo[y]==flo[pos]+1&&edge[i].dis!=0&&d[y]==d[pos]+edge[i].cos)
            {
          		int data=dfs(y,min(dis,edge[i].dis));
          		if(data>0)
          		{
          			edge[i].dis-=data;
          			edge[i^1].dis+=data;
          			ans_cos+=edge[i].cos*data;
                	return data; 
            	}
          	}
        }
        return 0;
    }
    int dinic()
    {
        int ans=0;
        while(bfs())
        {
            memcpy(cur,head,sizeof(head));
            while(int data=dfs(s,0x3f3f3f3f))
                ans+=data;
        }
        return ans;
    }
    

    二、最小割

    ​实际上,最小割的应用很多:详见国家集训队胡伯涛论文:最小割模型在信息学中的应用

    1.最大权闭合子图

    ​ 闭合图:对于一个有向图(G),存在点集合(V),任取点(u)属于(V)(u)的出边的另一个点也属于(V),则为闭合图。

    ​ 最大闭合子图,即原图每个点有点权,求原图的闭合子图中最大点权和(点权可正可负)。

    ​ 转化:(S)向所有正权点建边,容量为点权,所有负权点向(T)建边,容量为点权绝对值,原边容量为(Inf)

    ​ 最大权(=)正权和(-)最大流。一个通俗易懂的证明

    ​ 变式:([CEOI2008]order)(下附代码):将原边容量改为租金即可(因为租用与否仅和此工序有关)

    #include<bits/stdc++.h>
    using namespace std;
    inline int read()
    {
        int f=1,w=0;char x=0;
        while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();}
        while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();}
        return w*f;
    }
    const int N=300010;
    int n,m,sum,s,t;
    int head[N],num_edge,d[N],cur[N];
    struct {int next,to,dis;} edge[N*10];
    inline void add(int from,int to,int dis)
    {
    	edge[++num_edge].next=head[from];
    	edge[num_edge].dis=dis;
    	edge[num_edge].to=to;
    	head[from]=num_edge;
    }
    inline bool bfs()
    {
    	memset(d,0,sizeof(d));d[s]=1;
    	queue<int> q;q.push(s);
    	while(!q.empty())
    	{
    		int x=q.front();q.pop();
    		for(int i=head[x];i!=-1;i=edge[i].next)
    			if(!d[edge[i].to]&&edge[i].dis)
    				d[edge[i].to]=d[x]+1,q.push(edge[i].to);
    	}
    	return d[t]?1:0;
    }
    inline int dfs(int pos,int dis)
    {
    	if(pos==t) return dis;
    	for(int i=cur[pos];i!=-1;i=edge[i].next)
    	{
    		cur[pos]=i;
    		if(d[edge[i].to]==d[pos]+1&&edge[i].dis>0)
    		{
    			int data=dfs(edge[i].to,min(edge[i].dis,dis));
    			if(data>0)
    			{
    				edge[i].dis-=data;
    				edge[i^1].dis+=data;
    				return data;
    			}freopen("A.in","r",stdin);//12
    		}
    	}
    	return 0;
    }
    inline int Dinic()
    {
    	int ans=0;
    	while(bfs())
    	{
    		memcpy(cur,head,sizeof(cur));
    		while(int data=dfs(s,0x3f3f3f3f))
    			ans+=data;
    	}
    	return ans;
    }
    int main(){
    #ifndef ONLINE_JUDGE
        freopen("A.in","r",stdin);
    #endif
    	n=read(),m=read();s=0,t=n+m+2;
    	num_edge=-1;memset(head,-1,sizeof(head));
    	for(int i=1;i<=n;i++)
    	{
    		int w=read(),c=read();sum+=w;
    		add(0,i,w),add(i,0,0);
    		for(int j=1;j<=c;j++)
    		{
    			int id=read();w=read();
    			add(i,id+n,w),add(id+n,i,0);
    		}
    	}
    	for(int i=1,w;i<=m;i++)
    		w=read(),add(i+n,t,w),add(t,i+n,0);
    	printf("%d",sum-Dinic());
    }
    

    三、最小路径覆盖

    ​ 转化:先拆点为(v,v{'}),原图中(u)(v)有边,连(u,v{'})即可。最小路径(=)点数(-)最大匹配。

    ​ 变式:([SDOI2010])星际竞速:考虑跳跃,无论从哪个点跳到(i),花费均为(A[i]),所以可以建(S)(v{'}),容量为(A[i])的边。(下附代码)

    #include<bits/stdc++.h>
    using namespace std;
    inline int read()
    {
        int f=1,w=0;char x=0;
        while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();}
        while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();}
        return w*f;
    }
    const int N=300010;
    int n,m,sum,s,t,flo[N],v[N],ans;
    int head[N],num_edge,d[N],cur[N];
    struct {int next,to,dis,cos;} edge[N*10];
    inline void add(int from,int to,int dis,int cos)
    {
    	edge[++num_edge].next=head[from];
    	edge[num_edge].dis=dis;
    	edge[num_edge].cos=cos;
    	edge[num_edge].to=to;
    	head[from]=num_edge;
    }
    inline bool bfs()
    {
    	memset(d,0x3f3f3f3f,sizeof(d));
    	memset(v,0,sizeof(v));memset(flo,0,sizeof(flo));
    	queue<int> q;q.push(s);flo[s]=1;v[s]=1;d[s]=0;
    	while(!q.empty())
    	{
    		int x=q.front();q.pop();v[x]=0;
    		for(int i=head[x];i!=-1;i=edge[i].next)
    			if(d[edge[i].to]>d[x]+edge[i].cos&&edge[i].dis>0)
    			{
    				d[edge[i].to]=d[x]+edge[i].cos;
    				flo[edge[i].to]=flo[x]+1;
    				if(!v[edge[i].to]) q.push(edge[i].to),v[edge[i].to]=1;
    			}
    	}
    	return d[t]<0x3f3f3f3f?1:0;
    }
    inline int dfs(int pos,int dis)
    {
    	if(pos==t) return dis;
    	for(int i=cur[pos];i!=-1;i=edge[i].next)
    	{
    		int y=edge[i].to;cur[pos]=i;
    		if(d[y]==d[pos]+edge[i].cos&&edge[i].dis!=0&&flo[y]==flo[pos]+1)
    		{
    			int data=dfs(y,min(edge[i].dis,dis));
    			if(data>0)
    			{
    				edge[i].dis-=data;
    				edge[i^1].dis+=data;
    				ans+=data*edge[i].cos;
    				return data;
    			}
    		}
    	}
    	return 0;
    }
    inline int Dinic()
    {
    	while(bfs())
    	{
    		memcpy(cur,head,sizeof(cur));
    		while(int data=dfs(s,0x3f3f3f3f));
    	}
    	return ans;
    }
    int main(){
    #ifndef ONLINE_JUDGE
        freopen("A.in","r",stdin);//12
    	//freopen("B.in","r",stdin);//6
    	//freopen("C.in","r",stdin);//230
    #endif
    	n=read(),m=read();s=0,t=(n<<1)+1;
    	num_edge=-1;memset(head,-1,sizeof(head));
    	for(int i=1,w;i<=n;i++)
    		w=read(),add(s,i+n,1,w),add(i+n,s,0,-w);
    	for(int i=1;i<=m;i++)
    	{
    		int u=read(),v=read(),w=read();
    		if(v<u) swap(u,v);
    		add(u,v+n,1,w),add(v+n,u,0,-w);
    	}
    	for(int i=1;i<=n;i++)
    		add(s,i,1,0),add(i,s,0,0),add(i+n,t,1,0),add(t,i+n,0,0);
    	printf("%d",Dinic());
    }
    

    四、各种奇特的建图

    1、与时间有关:

    其实这样的题以前考过一次,好像那次(Itst)是全场唯一一个切那道题的,现在再放一道题:

    传送门:(Luogu[SCOI2007])修车

    显然和时间有关,要分时间段建边。

    分析性质,对于一个修车工,假设其修的车用时分别为:(T_1,T_2……,T_p),那么总等待时间为:(T_p*1+T_{p-1}*2+……+T_1*p)

    可以看出一个修车工所修的第倒数(k)个车对答案的贡献为(k*T)。所以我们可以将每个修车工拆为(N)个时间段,表示倒数几个修,将每个车和拆开的点建边,费用为时间段编号乘花费时间即可。(下附代码)

    #include<bits/stdc++.h>
    using namespace std;
    inline int read()
    {
        int f=1,w=0;char x=0;
        while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();}
        while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();}
        return w*f;
    }
    const int N=6000,M=100010;
    int n,m,num_edge,s,t,ans;
    int d[N],head[N],v[N],flo[N],cur[N];
    struct Edge{int next,to,dis,cos;} edge[M];
    inline void add(int from,int to,int dis,int cos)
    {
    	edge[++num_edge].next=head[from];
    	edge[num_edge].dis=dis;
    	edge[num_edge].cos=cos;
    	edge[num_edge].to=to;
    	head[from]=num_edge;
    }
    inline bool bfs()
    {
    	memset(d,0x3f3f3f3f,sizeof(d));
    	memset(v,0,sizeof(v));memset(flo,0,sizeof(flo));
    	queue<int> q;q.push(s);d[s]=0,v[s]=flo[s]=1;
    	while(!q.empty())
    	{
    		int x=q.front();q.pop();v[x]=0;
    		for(int i=head[x];i!=-1;i=edge[i].next)
    			if(d[edge[i].to]>d[x]+edge[i].cos&&edge[i].dis>0)
    			{
    				d[edge[i].to]=d[x]+edge[i].cos;
    				flo[edge[i].to]=flo[x]+1;
    				if(!v[edge[i].to]) q.push(edge[i].to),v[edge[i].to]=1;
    			}
    	}
    	return d[t]<0x3f3f3f3f?1:0;
    }
    inline int dfs(int pos,int dis)
    {
    	if(pos==t) return dis;
    	for(int i=cur[pos];i!=-1;i=edge[i].next)
    	{
    		int y=edge[i].to;cur[pos]=i;
    		if(flo[y]==flo[pos]+1&&d[y]==d[pos]+edge[i].cos&&edge[i].dis>0)
    		{
    			int data=dfs(y,min(dis,edge[i].dis));
    			if(data>0)
    			{
    				edge[i].dis-=data;
    				edge[i^1].dis+=data;
    				ans+=edge[i].cos*data;
    				return data;
    			}
    		}
    	}
    	return 0;
    }
    inline int Dinic()
    {
    	while(bfs())
    	{
    		memcpy(cur,head,sizeof(head));
    		while(int data=dfs(s,0x3f3f3f3f));
    	}
    	return ans;
    }
    int main(){
    #ifndef ONLINE_JUDGE
        freopen("A.in","r",stdin);//Ans=1.50
    #endif
    	num_edge=-1;memset(head,-1,sizeof(head));
    	m=read(),n=read();s=0,t=m*n+n+1;
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    		{
    			int T=read();
    			for(int k=1;k<=n;k++)
    				add(i,n*j+k,1,k*T),add(n*j+k,i,0,-k*T);
    		}
    	for(int i=1;i<=n;i++) add(s,i,1,0),add(i,s,0,0);
    	for(int i=1;i<=n*m;i++) add(i+n,t,1,0),add(t,i+n,0,0);
    	printf("%.2lf",(double)Dinic()/(double)n);
    }
    
    

    (To) (be) (continued...)

  • 相关阅读:
    ok
    Android设备激活量超10亿 3个月增长1亿台
    JavaWeb--文件的上传和下载
    JavaWeb--Listener
    JavaWeb--JSTL
    JavaWeb--自定义标签4--EL自定义函数
    JavaWeb--自定义标签4--带父标签的自定义标签
    codeforces 437B. The Child and Set 解题报告
    codeforces 437A. The Child and Homework 解题报告
    codeforces 435 B. Pasha Maximizes 解题报告
  • 原文地址:https://www.cnblogs.com/wo-shi-zhen-de-cai/p/11230525.html
Copyright © 2020-2023  润新知