有源汇上下界网络流简单总结

我们考虑普通的网络流只有上界,加个下界怎么做?

1.我们先把下界的限制消掉,就是使得每条边强制流上他下界的流量,这样限制得到了转化(只有上界),但是并不一定满足流量守恒

2.调整流量,使得他变成一个可行流,我们考虑建虚拟源汇点SS,TT,然后考虑如果某个点流量盈余a[i]<0就让他连一条向TT的容量上限为abs(a[i])的边,a[i]>0同理,然后我们跑一遍最大流,只要使得最大流为\sum_{a[i]>0} a[i]就保证原网络有可行流(由于该网络有源汇,所以要使T->S连一条容量上限为INF的边来保证每个点都满足流量守恒)

3.最后一步就是可行流转化为最大/最小流,我们只要在残量网络上跑一遍最大流就行了(求最大流),如果求最小流,只要T->S跑一遍最大流就行了

模板

 void add(int x,int y,int l,int r) {
        edge[k].u=y; edge[k].to=head[x]; edge[k].l=l; edge[k].r=r; edge[k].w=edge[k].r-edge[k].l; head[x]=k++;
        edge[k].u=x; edge[k].to=head[y]; edge[k].l=0; edge[k].r=0; edge[k].w=0; head[y]=k++;
    }
    void add_new(int x,int y,int z){
        edge[k].u=y; edge[k].to=head[x]; edge[k].w=z; head[x]=k++;
        edge[k].u=x; edge[k].to=head[y]; edge[k].w=0; head[y]=k++;
    }
 bool bfs(int S,int T) {
        queue<int>q;
        q.push(S);
        memset(arrange,0,sizeof(arrange));
        arrange[S]=1;
        while (!q.empty()) {
            int v=q.front(); q.pop(); if (v==T) return 1;
            for (int i=head[v];i!=-1;i=edge[i].to) {
                int u=edge[i].u;
                if (edge[i].w&&!arrange[u]) {
                    arrange[u]=arrange[v]+1;
                    q.push(u);
                }
            }
        }
        return 0;
    }
    int dfs(int now,int maxlow,int t) {
        if (now==t) return maxlow;
        int ret=0;
        for (int &i=cur[now];i!=-1;i=edge[i].to) {
            int u=edge[i].u;
            if (arrange[u]==arrange[now]+1&&edge[i].w) {
                int f=dfs(u,min(maxlow-ret,edge[i].w),t); ret+=f;
                edge[i].w-=f; edge[i^1].w+=f;
                if (ret==maxlow) return ret;
            }
        }
        return ret;
    }
    int dinic(int S,int T) {
        int ans=0;
        while (bfs(S,T)) {
            memcpy(cur,head,sizeof(head));
            ans+=dfs(S,INF,T);
        }
        return ans;
    }
   //以上为最大流dinic
    void upper_lower_maxflow(int S,int T,int n) {//n:总结点个数
        add(T,S,0,INF);
    	for (int i=1;i<=n+2;i++) {
    		for (int j=head[i];j!=-1;j=edge[j].to)
            {
    			ret_flow[i]-=edge[j].l; ret_flow[edge[j].u]+=edge[j].l;
            }
        }//计算节点盈余,在这里S=n+1,T=n+2
        int SS=n+3,TT=n+4;
        int maxflow=0;
        for (int i=1;i<=n+2;i++) 
            if (ret_flow[i]>0) add_new(SS,i,ret_flow[i]),maxflow+=ret_flow[i]; else add_new(i,TT,-ret_flow[i]);
        if (maxflow!=dinic(SS,TT))  {
            puts("JIONG!"); return;
        }
        maxflow=0;
        for (int i=head[T];i!=-1;i=edge[i].to) if (edge[i].u>=S) edge[i].w=0,edge[i^1].w=0;
        for (int i=head[S];i!=-1;i=edge[i].to) if (edge[i].u>=S) edge[i].w=0,edge[i^1].w=0; //去掉S-T的边
        dinic(T,S);//这里求最小流
 		for (int i=head[T];i!=-1;i=edge[i].to) {
        maxflow+=edge[i].w+edge[i^1].l;
        }
        printf("%d\n",maxflow);
    }

清理雪道

由于边可以重复,所以S向每个点连一条下界为0,上界为INF的边,每个点向T连一条下界为0,上界为INF的边,如果两点u->v在原图上有边,那么就连一条u->v下界为1,上界为INF的边,最后直接跑上下界网络流就行了

// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
const int N=300;
const int M=50010;
const int INF=0x3f3f3f3f;
struct node{
    int u,to,l,r,w;
};
node edge[M<<1];
int arrange[N],head[N],a[N],cur[N],ret_flow[N];
int n,cnt,S,T,k,x,y;
    void add(int x,int y,int l,int r) {
        edge[k].u=y; edge[k].to=head[x]; edge[k].l=l; edge[k].r=r; edge[k].w=edge[k].r-edge[k].l; head[x]=k++;
        edge[k].u=x; edge[k].to=head[y]; edge[k].l=0; edge[k].r=0; edge[k].w=0; head[y]=k++;
    }
    void add_new(int x,int y,int z){
        edge[k].u=y; edge[k].to=head[x]; edge[k].w=z; head[x]=k++;
        edge[k].u=x; edge[k].to=head[y]; edge[k].w=0; head[y]=k++;
    }
    bool bfs(int S,int T) {
        queue<int>q;
        q.push(S);
        memset(arrange,0,sizeof(arrange));
        arrange[S]=1;
        while (!q.empty()) {
            int v=q.front(); q.pop(); if (v==T) return 1;
            for (int i=head[v];i!=-1;i=edge[i].to) {
                int u=edge[i].u;
                if (edge[i].w&&!arrange[u]) {
                    arrange[u]=arrange[v]+1;
                    q.push(u);
                }
            }
        }
        return 0;
    }
    int dfs(int now,int maxlow,int t) {
        if (now==t) return maxlow;
        int ret=0;
        for (int &i=cur[now];i!=-1;i=edge[i].to) {
            int u=edge[i].u;
            if (arrange[u]==arrange[now]+1&&edge[i].w) {
                int f=dfs(u,min(maxlow-ret,edge[i].w),t); ret+=f;
                edge[i].w-=f; edge[i^1].w+=f;
                if (ret==maxlow) return ret;
            }
        }
        return ret;
    }
    int dinic(int S,int T) {
        int ans=0;
        while (bfs(S,T)) {
            memcpy(cur,head,sizeof(head));
            ans+=dfs(S,INF,T);
        }
        return ans;
    }
    void upper_lower_maxflow(int S,int T) {
    	for (int i=1;i<=n+2;i++) {
    		for (int j=head[i];j!=-1;j=edge[j].to)
            {
    			ret_flow[i]-=edge[j].l; ret_flow[edge[j].u]+=edge[j].l;
            }
        }
        int SS=++cnt,TT=++cnt;
        int maxflow=0;
        for (int i=1;i<=n+2;i++) 
            if (ret_flow[i]>0) add_new(SS,i,ret_flow[i]),maxflow+=ret_flow[i]; else add_new(i,TT,-ret_flow[i]);
        if (maxflow!=dinic(SS,TT))  {
            printf("%d\n",-1); return;
        }
        maxflow=0;
        for (int i=head[T];i!=-1;i=edge[i].to) if (edge[i].u>=S) edge[i].w=0,edge[i^1].w=0;
        for (int i=head[S];i!=-1;i=edge[i].to) if (edge[i].u>=S) edge[i].w=0,edge[i^1].w=0; 
        for (int i=1;i<=n+2;i++) {
//			for (int j=head[i];j!=-1;j=edge[j].to) printf("%d %d %d\n",i,edge[j].u,edge[j].w);
        } 
        dinic(T,S);
        for (int i=head[S];i!=-1;i=edge[i].to) {
        maxflow+=edge[i^1].w+edge[i].l;
        }
        printf("%d\n",maxflow);
    }
    
int main() {
    memset(head,-1,sizeof(head)); k=0;
    scanf("%d",&n);
    cnt=n+2; 
    S=n+1; T=n+2;
    for (int i=1;i<=n;i++) {
        add(S,i,0,INF); add(i,T,0,INF);
    }
    for (int i=1;i<=n;i++) {
        scanf("%d",&x);
        for (int j=1;j<=x;j++) {
            scanf("%d",&y); add(i,y,1,INF);
        }
    }
    add(T,S,0,INF);
    upper_lower_maxflow(S,T);
}

士兵占领

考虑二分图,把行与列分别作为行与列的两个点集,如果(i,j)上没有障碍,那么就i->j连一条下界为0,上界为1的边

同时S->i连一条下界为Li,上界为INF的边,j->T连一条下界为Ci,上界为INF的边,跑一遍上下界网络流

i,j分别为行与列经过映射的点

// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
const int N=300;
const int M=50010;
const int INF=0x3f3f3f3f;
struct node{
	int u,to,l,r,w;
};
node edge[M<<1];
int arrange[N],head[N],a[N],cur[N],ret_flow[N];
int b[110][110];
int n,cnt,S,T,k,x,y,m,k_sum;
	void add(int x,int y,int l,int r) {
		edge[k].u=y; edge[k].to=head[x]; edge[k].l=l; edge[k].r=r; edge[k].w=edge[k].r-edge[k].l; head[x]=k++;
		edge[k].u=x; edge[k].to=head[y]; edge[k].l=0; edge[k].r=0; edge[k].w=0; head[y]=k++;
	}
	void add_new(int x,int y,int z){
		edge[k].u=y; edge[k].to=head[x]; edge[k].w=z; head[x]=k++;
		edge[k].u=x; edge[k].to=head[y]; edge[k].w=0; head[y]=k++;
	}
    bool bfs(int S,int T) {
        queue<int>q;
        q.push(S);
        memset(arrange,0,sizeof(arrange));
        arrange[S]=1;
        while (!q.empty()) {
            int v=q.front(); q.pop(); if (v==T) return 1;
            for (int i=head[v];i!=-1;i=edge[i].to) {
                int u=edge[i].u;
                if (edge[i].w&&!arrange[u]) {
                    arrange[u]=arrange[v]+1;
                    q.push(u);
                }
            }
        }
        return 0;
    }
    int dfs(int now,int maxlow,int t) {
        if (now==t) return maxlow;
        int ret=0;
        for (int &i=cur[now];i!=-1;i=edge[i].to) {
            int u=edge[i].u;
            if (arrange[u]==arrange[now]+1&&edge[i].w) {
                int f=dfs(u,min(maxlow-ret,edge[i].w),t); ret+=f;
                edge[i].w-=f; edge[i^1].w+=f;
                if (ret==maxlow) return ret;
            }
        }
        return ret;
    }
    int dinic(int S,int T) {
        int ans=0;
        while (bfs(S,T)) {
            memcpy(cur,head,sizeof(head));
            ans+=dfs(S,INF,T);
        }
        return ans;
    }
    void upper_lower_maxflow(int S,int T,int n) {
    	for (int i=1;i<=n+2;i++) {
    		for (int j=head[i];j!=-1;j=edge[j].to)
			{
    			ret_flow[i]-=edge[j].l; ret_flow[edge[j].u]+=edge[j].l;
			}
		}
	    int SS=n+3,TT=n+4;
	    int maxflow=0;
		for (int i=1;i<=n+2;i++) 
			if (ret_flow[i]>0) add_new(SS,i,ret_flow[i]),maxflow+=ret_flow[i]; else add_new(i,TT,-ret_flow[i]);
		if (maxflow!=dinic(SS,TT))  {
		    puts("JIONG!"); return;
		}
		maxflow=0;
		for (int i=head[T];i!=-1;i=edge[i].to) if (edge[i].u>=S) edge[i].w=0,edge[i^1].w=0;
		for (int i=head[S];i!=-1;i=edge[i].to) if (edge[i].u>=S) edge[i].w=0,edge[i^1].w=0; 
//		for (int i=head[T];i!=-1;i=edge[i].to) printf("%d %d %d\n",T,edge[i].u,edge[i].w);
		dinic(T,S);
 		for (int i=head[T];i!=-1;i=edge[i].to) {
		maxflow+=edge[i].w+edge[i^1].l;
		}
		printf("%d\n",maxflow);
	}
	
int main() {
	memset(head,-1,sizeof(head)); k=0;
	scanf("%d%d%d",&n,&m,&k_sum);
	S=n+m+1; T=n+m+2;
	for (int i=1;i<=n;i++) {
		scanf("%d",&x);  add(S,i,x,INF);
	}	
	for (int i=1;i<=m;i++) {
		scanf("%d",&x);  add(n+i,T,x,INF);
	}
	for (int i=1;i<=k_sum;i++) {
		scanf("%d%d",&x,&y); b[x][y]=1;
	}
	for (int i=1;i<=n;i++) {
		for (int j=1;j<=m;j++)
		 if (!b[i][j]) add(i,n+j,0,1);
	}
	add(T,S,0,INF);
	upper_lower_maxflow(S,T,n+m);
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值