• [学习笔记]网络流24题


    不按24题的顺序,按我做题的顺序

    1、飞行员配对方案问题

    建个图,跑遍匈牙利,让飞行员给其对应的飞机连一条边

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=10000+10;
    int n,m,e,head[maxn],to[maxn<<1],nxt[maxn<<1],vis[maxn],tot,ans,match[maxn];
    inline void add(int x,int y){
        to[++tot]=y;
        nxt[tot]=head[x];
        head[x]=tot;
    }
    int dfs(int x){
        for(int i=head[x];i;i=nxt[i]){
            int y=to[i];
            if(!vis[y]){
                vis[y]=1;
                if(!match[y]||dfs(match[y])){
                    match[y]=x;return 1;
                }
            }
        }
        return 0;
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        int x,y;scanf("%d%d",&x,&y);
        while(x!=-1&&y!=-1){
            if(x<=n&&y<=n+m) add(x,y);
            scanf("%d%d",&x,&y);
        }
        for(int i=1;i<=n;i++){
            memset(vis,0,sizeof(vis));
            if(dfs(i)) ans++;
        }
        if(ans){
            printf("%d
    ",ans);
            for(int i=n+1;i<=m;i++)
                if(match[i]) printf("%d %d
    ",match[i],i);
        }
        else printf("No Solution!
    ");
        return 0;
    }
    

    2、试题库问题

    相当于超级源连一条容量为试卷题目总数的边,每张试卷对题目连一条容量为(1)的边,题目对超级汇连一条容量为(1)的边

    方案嘛就找一下残量网络中流量为(0)的正向边,然后输出一下它的朝向(e[i].to-k)

    而且顺带说一句,每一张试卷的题目总数不一定等于它连出的边!

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=100000+10;
    const int inf=0x3f3f3f3f;
    int k,n,m,s,t,head[maxn],tot=1;
    int dep[maxn],cur[maxn];
    
    struct node{
    	int to,next,val;
    }e[maxn];
    
    inline void add(int x,int y,int w){
    	e[++tot].to=y;
    	e[tot].val=w;
    	e[tot].next=head[x];
    	head[x]=tot;
    }
    
    int bfs(int s,int t){
    	queue<int> q;
    	for(int i=0;i<=t;i++)
    		cur[i]=head[i];
    	memset(dep,0x7f,sizeof(dep));
    	dep[s]=0;
    	q.push(s);
    	int u,v;
    	while(!q.empty()){
    		u=q.front(),q.pop();
    		for(int i=head[u];i;i=e[i].next){
    			v=e[i].to;
    			if(dep[v]>inf&&e[i].val){
    				dep[v]=dep[u]+1;
    				q.push(v);
    			}
    		}
    	}
    	return dep[t]<inf;
    }
    
    int dfs(int now,int t,int limit){
    	if(!limit||now==t) return limit;
    	int flow=0,f,v;
    	for(int i=cur[now];i;i=e[i].next){
    		cur[now]=i;v=e[i].to;
    		if(dep[v]==dep[now]+1&&(f=dfs(v,t,min(limit,e[i].val)))){
    			flow+=f;
    			limit-=f;
    			e[i].val-=f;
    			e[i^1].val+=f;
    			if(!limit) break;
    		}
    	}
    	return flow;
    }
    
    int Dinic(){
    	int maxflow=0;
    	while(bfs(s,t))
    		maxflow+=dfs(s,t,inf);
    	return maxflow;
    }
    
    int main()
    {
    	scanf("%d%d",&k,&n);
    	s=0;t=n+k+1;
    	int x,y,w,p;
    	for(int i=1;i<=k;i++){
    		scanf("%d",&w);
    		add(s,i,w);add(i,s,0);
    		m+=w;
    	}
    	for(int i=1;i<=n;i++){
    		scanf("%d",&p);
    		for(int j=1;j<=p;j++){
    			scanf("%d",&x);
    			add(x,i+k,1);add(i+k,x,0);
    		}
    	}
    	for(int i=1;i<=n;i++){
    		add(i+k,t,1);add(t,i+k,0);
    	}
    	int maxflow=Dinic();
    	if(maxflow==m){
    		for(int x=1;x<=k;x++){
    			printf("%d:",x);
    			for(int i=head[x];i;i=e[i].next){
    				if(!e[i].val&&e[i].to>0){
    					printf("%d ",e[i].to-k);
    				}
    			}
    			printf("
    ");
    		}
    	}
    	else printf("No Solution
    ");
    	return 0;
    }
    

    3、最小路径覆盖问题

    我用二分图做的,网络流我不知道。二分图建图好建,但是方案难输出
    .
    方案就是每次找到拆点的(i'),把它映射到(match[i]),开个(vis)数组存一下标记

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=500+10;
    int n,m,match[maxn],vis[maxn],ans;
    vector<int> G[maxn];
    
    int dfs(int x){
    	int y;
    	for(int i=0;i<G[x].size();i++){
    		y=G[x][i];
    		if(!vis[y]){
    			vis[y]=1;
    			if(!match[y]||dfs(match[y])){
    				match[y]=x;match[x]=y;
    				return 1;
    			}
    		}
    	}
    	return 0;
    }
    
    void print(int x){
    	x+=n;
    	for(int i=x;i;i=match[i-n]){
    		vis[i-n]=1;
    		printf("%d ",i-n);
    	}
    	printf("
    ");
    }
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	int x,y;
    	for(int i=1;i<=m;i++){
    		scanf("%d%d",&x,&y);
    		G[x].push_back(y+n);
    	}
    	ans=n;
    	for(int i=1;i<=n;i++){
    		memset(vis,0,sizeof(vis));
    		if(dfs(i)) ans--;
    	}
    	memset(vis,0,sizeof(vis));
    	for(int i=1;i<=n;i++)
    		if(!vis[i]) print(i);
    	printf("%d
    ",ans);
    	return 0;
    }
    

    4、魔术球问题

    这题贪心随便水,但是网络流可做

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=100000+10;
    const int inf=0x3f3f3f3f;
    int n,m=5000,s=0,t=10000,head[maxn],tot=1,cnt;
    int dep[maxn],cur[maxn],vis[maxn],nxt[maxn],ans[maxn];
    
    struct node{
    	int to,next,val;
    }e[maxn];
    
    inline void add(int x,int y,int w){
    	e[++tot].to=y;
    	e[tot].val=w;
    	e[tot].next=head[x];
    	head[x]=tot;
    }
    
    int bfs(int s,int t){
    	queue<int> q;
    	for(int i=0;i<=t;i++)
    		cur[i]=head[i];
    	memset(dep,-1,sizeof(dep));
    	dep[s]=0;
    	q.push(s);
    	int u,v;
    	while(!q.empty()){
    		u=q.front(),q.pop();
    		for(int i=head[u];i;i=e[i].next){
    			v=e[i].to;
    			if(dep[v]==-1&&e[i].val){
    				dep[v]=dep[u]+1;
    				q.push(v);
    			}
    		}
    	}
    	return dep[t]!=-1;
    }
    
    int dfs(int now,int t,int limit){
    	if(!limit||now==t) return limit;
    	int flow=0,f,v;
    	for(int i=cur[now];i;i=e[i].next){
    		cur[now]=i;v=e[i].to;
    		if(dep[v]==dep[now]+1&&(f=dfs(v,t,min(limit,e[i].val)))){
    			flow+=f;
    			limit-=f;
    			e[i].val-=f;
    			e[i^1].val+=f;
    			if(!limit) break;
    		}
    	}
    	return flow;
    }
    
    int Dinic(){
    	int maxflow=0;
    	while(bfs(s,t))
    		maxflow+=dfs(s,t,inf);
    	return maxflow;
    }
    
    int main()
    {
    	scanf("%d",&n);
    	int now=0,flow=0;
    	while(1){
    		flow++;now++;
    		for(int i=1;i<now;i++)
    			if(sqrt(i+now)==(int)sqrt(i+now)){
    				add(i,now+m,1);
    				add(now+m,i,0);
    			}
    		add(s,now,1);
    		add(now,s,0);
    		add(now+m,t,1);
    		add(t,now+m,0);
    		flow-=Dinic();
    		if(flow>n) break;
    	}
    	printf("%d
    ",--now);
    	for(int u=1;u<=now;u++){
    		for(int i=head[u];i;i=e[i].next){
    			if(!e[i].val){
    				nxt[u]=e[i].to-m;
    				break;
    			}
    		}
    	}
    	for(int i=1;i<=now;i++){
    		if(!vis[i]){
    			for(int u=i;u!=-m;u=nxt[u]){
    				vis[u]=1;
    				printf("%d ",u);
    			}
    			printf("
    ");
    		}
    	}
        return 0;
    }
    

    5、方格取数问题

    奇偶图弄一下,连容量为方格中的数,然后在相邻方格连一条容量为(inf)的边

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=100000+10;
    const int inf=1e9;
    int n,m,s,t;
    struct Edge{
        int to,val,next;
    }e[maxn<<1];
    int head[maxn],cur[maxn],deep[maxn],tot=1;
    int x[4]={0,0,1,-1},y[4]={1,-1,0,0};
    
    void add(int x,int y,int w,int flag)
    {
        e[++tot].to=y;
        e[tot].next=head[x];
        head[x]=tot;
        if(flag) e[tot].val=w;
    }
    
    int bfs(int s,int t)
    {
        queue<int> q;
        for(int i=1;i<=n*m+2;i++)
            cur[i]=head[i];
        memset(deep,0x7f,sizeof(deep));
        deep[s]=0;
        q.push(s);
        while(!q.empty())
        {
            int u=q.front();q.pop();
            for(int i=head[u];i;i=e[i].next)
            {
                int y=e[i].to;
                if(deep[y]>inf&&e[i].val)
                {
                    deep[y]=deep[u]+1;
                    q.push(y);
                }
            }
        }
        if(deep[t]<inf) 
            return 1;
        return 0;
    }
    
    int dfs(int now,int t,int limit)
    {
        if(!limit||now==t) return limit;
        int f,flow=0;
        for(int i=cur[now];i;i=e[i].next){
            cur[now]=i;
            if(deep[e[i].to]==deep[now]+1&&(f=dfs(e[i].to,t,min(limit,e[i].val)))){
                flow+=f;
                limit-=f;
                e[i].val-=f;
                e[i^1].val+=f;
                if(!limit) break;
            }
        }
        return flow;
    }
    
    int Dinic(){
        int maxflow=0;
        while(bfs(s,t))
            maxflow+=dfs(s,t,inf);
        return maxflow;
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);s=n*m+1,t=n*m+2;
        int sum=0;
        for(int i=1;i<=n;i++)
        	for(int j=1;j<=m;j++){
        		int x;scanf("%d",&x);sum+=x;
        		if((i+j)%2==1){add(s,(i-1)*m+j,x,1);add((i-1)*m+j,s,x,0);}
        		else {add((i-1)*m+j,t,x,1);add(t,(i-1)*m+j,x,0);}
    		}
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++){
    			if((i+j)%2==1){
    				for(int k=0;k<4;k++){
    					int u=i+x[k],v=j+y[k];
    					if(u<1||u>n||v<1||v>m) continue;
    					add((i-1)*m+j,(u-1)*m+v,inf,1);
    					add((u-1)*m+v,(i-1)*m+j,inf,0);
    				}
    			}
    		}
        printf("%d
    ",sum-Dinic());
        return 0;
    }
    

    6、圆桌问题

    比较经典的建图,类似试题库问题,经常结合二分答案+最大流判定(此题不用)

    // luogu-judger-enable-o2
    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=100000+10;
    const int inf=1e9;
    int n,m,s,t;
    struct Edge{
    	int to,val,next;
    }e[maxn<<1];
    int head[maxn],cur[maxn],deep[maxn],tot=1;
    
    void add(int x,int y,int w,int flag)
    {
    	e[++tot].to=y;
    	e[tot].next=head[x];
    	head[x]=tot;
    	if(flag) e[tot].val=w;
    }
    
    int bfs(int s,int t)
    {
    	queue<int> q;
    	for(int i=0;i<=n+m+1;i++)
    		cur[i]=head[i];
    	memset(deep,0x7f,sizeof(deep));
    	deep[s]=0;
    	q.push(s);
    	while(!q.empty())
    	{
    		int u=q.front();q.pop();
    		for(int i=head[u];i;i=e[i].next)
    		{
    			int y=e[i].to;
    			if(deep[y]>inf&&e[i].val)
    			{
    				deep[y]=deep[u]+1;
    				q.push(y);
    			}
    		}
    	}
    	if(deep[t]<inf) 
    		return 1;
    	return 0;
    }
    
    int dfs(int now,int t,int limit)
    {
    	if(!limit||now==t) return limit;
    	int f,flow=0;
    	for(int i=cur[now];i;i=e[i].next){
    		cur[now]=i;
    		if(deep[e[i].to]==deep[now]+1&&(f=dfs(e[i].to,t,min(limit,e[i].val)))){
    			flow+=f;
    			limit-=f;
    			e[i].val-=f;
    			e[i^1].val+=f;
    			if(!limit) break;
    		}
    	}
    	return flow;
    }
    
    int Dinic(){
    	int maxflow=0;
    	while(bfs(s,t))
    		maxflow+=dfs(s,t,inf);
    	return maxflow;
    }
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	s=0,t=n+m+1;
    	int x,sum=0;
    	for(int i=1;i<=n;i++){
    		scanf("%d",&x);
    		add(s,i,x,1);
    		add(i,s,x,0);
    		sum+=x;
    	}
    	for(int i=1;i<=m;i++){
    		scanf("%d",&x);
    		add(i+n,t,x,1);
    		add(t,i+n,x,0);
    	}
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++){
    			add(i,j+n,1,1);
    			add(j+n,i,1,0);
    		}
    	if(Dinic()==sum){
    		printf("1
    ");
    		for(int i=1;i<=n;i++){
    			for(int j=head[i];j;j=e[j].next)
    				if(e[j].to>n&&e[j].to<=n+m&&!e[j].val)
    					printf("%d ",e[j].to-n);
    			printf("
    ");
    		}
    	}
    	else printf("0
    ");
    	return 0;
    }
    

    8、孤岛营救问题

    看到题解全是状压+(bfs),我也忍不住打了一个状压+(bfs),一遍过此题

    #include <bits/stdc++.h>
    using namespace std;
    int n,m,p,k,s;
    int vis[11][11][1<<11];
    int mp[11][11][11][11];
    int now[11][11],nx[4]={1,-1,0,0},ny[4]={0,0,1,-1};
    
    struct point{
    	int x,y,sta,step;
    };
    
    int bfs(){
    	queue<point> q;
    	vis[0][0][0]=1;
    	q.push((point){1,1,0,0});
    	point u;int x,y,k;
    	while(!q.empty()){
    		u=q.front(),q.pop();
    		if(u.x==n&&u.y==m) return u.step;
    		for(k=0;k<4;k++){
    			x=u.x+nx[k];y=u.y+ny[k];
    			if(x<1||x>n||y<1||y>m||vis[x][y][u.sta|now[x][y]]) continue;
    			if(mp[u.x][u.y][x][y]==-1) continue;
    			if(mp[u.x][u.y][x][y]>0&&!(u.sta&(1<<mp[u.x][u.y][x][y]))) continue;
    			vis[x][y][u.sta|now[x][y]]=1;
    			q.push((point){x,y,u.sta|now[x][y],u.step+1});
    		}
    	}
    	return -1;
    	
    }
    
    int main()
    {
    	scanf("%d%d%d",&n,&m,&p);
    	scanf("%d",&k);
    	int x1,y1,x2,y2,g,x,y,q;
    	for(int i=0;i<k;i++){
    		scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&g);
    		if(!g) mp[x1][y1][x2][y2]=mp[x2][y2][x1][y1]=-1;
    		else mp[x1][y1][x2][y2]=mp[x2][y2][x1][y1]=g;
    	}
    	scanf("%d",&s);
    	for(int i=0;i<s;i++){
    		scanf("%d%d%d",&x,&y,&q);
    		now[x][y]|=1<<q;
    	}
    	printf("%d
    ",bfs());
    	return 0;
    }
    

    9、分配问题

    二分图最佳完美匹配,可以类似网络流跑二分图跑两遍费用流,容量全部为(1),涉及超级源超级汇费用为(0),其他情况费用为(c_{ij})

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=1000+10;
    const int maxm=20000+10;
    const int inf=0x3f3f3f3f;
    int n,s,t,a[maxn][maxn],head[maxn],tot=1,maxflow,mincost;
    int pre[maxn],last[maxn],flow[maxn],dis[maxn],vis[maxn];
    
    struct node{
    	int to,next,flow,val;
    }e[maxm];
    
    inline void add(int x,int y,int w,int f){
    	e[++tot].to=y;
    	e[tot].flow=w;
    	e[tot].val=f;
    	e[tot].next=head[x];
    	head[x]=tot;
    }
    
    int spfa(){
    	memset(dis,inf,sizeof(dis));
    	memset(flow,inf,sizeof(flow));
    	memset(vis,0,sizeof(vis));
    	queue<int> q;
    	q.push(s);dis[s]=0;vis[s]=1;pre[t]=-1;
    	int u,v;
    	while(!q.empty()){
    		u=q.front(),q.pop();vis[u]=0;
    		for(int i=head[u];i;i=e[i].next){
    			v=e[i].to;
    			if(e[i].flow&&dis[v]>dis[u]+e[i].val){
    				dis[v]=dis[u]+e[i].val;
    				flow[v]=min(flow[u],e[i].flow);
    				pre[v]=u;last[v]=i;
    				if(!vis[v]){
    					vis[v]=1;
    					q.push(v);
    				}
    			}
    		}
    	}
    	return pre[t]!=-1;
    }
    
    void mcmf(){
    	int now;
    	while(spfa()){
    		now=t;
    		maxflow+=flow[t];
    		mincost+=flow[t]*dis[t];
    		while(now!=s){
    			e[last[now]].flow-=flow[t];
    			e[last[now]^1].flow+=flow[t];
    			now=pre[now];
    		}
    	}
    }
    
    int main()
    {
    	scanf("%d",&n);
    	int f;s=0;t=n<<1|1;
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=n;j++) scanf("%d",&a[i][j]);
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=n;j++){
    			add(i,j+n,1,a[i][j]);add(j+n,i,0,-a[i][j]);
    		}
    	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);
    	}
    	mcmf();
    	printf("%d
    ",mincost);
    	tot=1;memset(head,0,sizeof(head));
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=n;j++){
    			add(i,j+n,1,-a[i][j]);add(j+n,i,0,a[i][j]);
    		}
    	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);
    	}
    	maxflow=mincost=0;
    	mcmf();
    	printf("%d
    ",-mincost);
    	return 0;
    }
    

    10、运输问题

    此题是上题的加强版,其他都一样,就是涉及超级源和超级汇的容量是输入的(a_i)(b_j),因为(sum_{i=1}^{m}a_i=sum_{j=1}^{n}b_j),所以就是求满流的情况下的最小费用和最大费用,跑两遍费用流

    最后就是最大费用边连成负边,最后(-mincost)就是最大费用

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=200+10;
    const int maxm=30000+10;
    const int inf=0x3f3f3f3f;
    int n,m,s,t,a[maxn],b[maxn],c[maxn][maxn],head[maxn],tot=1,maxflow,mincost;
    int dis[maxn],vis[maxn],flow[maxn],pre[maxn],last[maxn];
    
    struct node{
    	int to,next,flow,val;
    }e[maxm];
    
    inline int read(){
    	register int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    	return (f==1)?x:-x;
    }
    inline void add(int x,int y,int w,int f){
    	e[++tot].to=y;
    	e[tot].flow=w;
    	e[tot].val=f;
    	e[tot].next=head[x];
    	head[x]=tot;
    }
    
    int spfa(){
    	memset(dis,inf,sizeof(dis));
    	memset(flow,inf,sizeof(flow));
    	memset(vis,0,sizeof(vis));
    	queue<int> q;
    	q.push(s);dis[s]=0;vis[s]=1;pre[t]=-1;
    	int u,v;
    	while(!q.empty()){
    		u=q.front(),q.pop();vis[u]=0;
    		for(int i=head[u];i;i=e[i].next){
    			v=e[i].to;
    			if(e[i].flow>0&&dis[v]>dis[u]+e[i].val){
    				dis[v]=dis[u]+e[i].val;
    				flow[v]=min(flow[u],e[i].flow);
    				pre[v]=u;last[v]=i;
    				if(!vis[v]){
    					vis[v]=1;
    					q.push(v);
    				}
    			}
    		}
    	}
    	return pre[t]!=-1;
    }
    
    void mcmf(){
    	int now;
    	while(spfa()){
    		now=t;
    		maxflow+=flow[t];
    		mincost+=flow[t]*dis[t];
    		while(now!=s){
    			e[last[now]].flow-=flow[t];
    			e[last[now]^1].flow+=flow[t];
    			now=pre[now];
    		}
    	}
    }
    
    int main()
    {
    	n=read(),m=read();
    	s=0,t=n+m+1;
    	int f;
    	for(int i=1;i<=n;i++) a[i]=read();
    	for(int i=1;i<=m;i++) b[i]=read();
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++) c[i][j]=read();
    	for(int i=1;i<=n;i++) add(s,i,a[i],0),add(i,s,0,0);
    	for(int i=1;i<=m;i++) add(i+n,t,b[i],0),add(t,i+n,0,0);
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++) add(i,j+n,inf,c[i][j]),add(j+n,i,0,-c[i][j]);
    	mcmf();
    	printf("%d
    ",mincost);
    	tot=1;memset(head,0,sizeof(head));
    	for(int i=1;i<=n;i++) add(s,i,a[i],0),add(i,s,0,0);
    	for(int i=1;i<=m;i++) add(i+n,t,b[i],0),add(t,i+n,0,0);
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++) add(i,j+n,inf,-c[i][j]),add(j+n,i,0,c[i][j]);
    	maxflow=mincost=0;
    	mcmf();
    	printf("%d
    ",-mincost);
    	return 0;
    }
    
  • 相关阅读:
    简单线程池的设计
    MFC开发--截图工具
    Win32小游戏--蜘蛛纸牌
    C++语言动态创建对象
    C++语言类之间的关系
    c++语言虚函数实现多态的原理(更新版)
    5-24 c++语言之【基础知识】
    小细节--关于printf的输出问题
    Win32小游戏--贪吃蛇
    2020北大夏令营 终末游记
  • 原文地址:https://www.cnblogs.com/owencodeisking/p/9847627.html
Copyright © 2020-2023  润新知