• 网络流专题 模板 + 例题 (Going Home POJ


    最大流

    Dinic

           建图时必须要建一条流量为0的反向边:建立反向边是为了增加重新调整的机会,即保障解是最优的。

    应用场景:

         1、裸的最大流

         2、二分图的最大匹配:建一个点S,连到二分图的集合A中;建一个点T,连到二分图的集合B中。再将所有的集合A中的点与       集合B中的点相连。全部边权设为1,跑一遍最大流,结果即为二分图的最大匹配。

         3、最小割:图中所有的割中,边权值和最小的割为最小割。  在单源单汇流量图中,最大流等于最小割。

         4、求最大权闭合图(这里):权值=正点权之和-最小割

    #include<bits/stdc++.h>
    #define ll long long
    #define MOD 998244353 
    #define INF 0x3f3f3f3f
    #define mem(a,x) memset(a,x,sizeof(a))  
    #define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    using namespace std;
    
    const int maxn= 200 + 5;  //点
    const int maxm= 2000 + 5;  //边
    int n,m,cnt=0;
    int head[maxn];
    int dis[maxn];
    struct node{
    	int e,v,nxt;
    }edge[maxm<<1];
    
    inline void addl(int u,int v,int w)
    {
    	edge[cnt].e=v;
    	edge[cnt].v=w;
    	edge[cnt].nxt=head[u];
    	head[u]=cnt++;
    }
    bool bfs()
    {
    	mem(dis,-1);
    	dis[1]=0;
    	queue<int>q;
    	q.push(1);
    	while(!q.empty()){
    		int r=q.front();
    		q.pop();
    		for(int i=head[r];i!=-1;i=edge[i].nxt)
    		{
    			int j=edge[i].e;
    			if(dis[j]==-1&&edge[i].v)
    			{
    				dis[j]=dis[r]+1;
    				q.push(j);
    			}
    		}
    	}
    	return dis[n]!=-1;
    }
    int dfs(int u,int flo)
    {
    	if(u==n)return flo;
    	int detla=flo;
    	for(int i=head[u];i!=-1;i=edge[i].nxt)
    	{
    		int v=edge[i].e;
    		if(dis[v]==(dis[u]+1)&&edge[i].v>0)
    		{
    			int d=dfs(v,min(detla,edge[i].v));
    			edge[i].v-=d;
    			edge[i^1].v+=d;
    			detla-=d;
    			if(detla==0)break;
    		}
    	}
    	return flo-detla;
    }
    int Dinic()
    {
    	int ans=0;
    	while(bfs())
    	{
    		ans+=dfs(1,INF);
    	}
    	return ans;
    }
    int main()
    {
    	while(~scanf("%d %d",&n,&m)){
    		mem(head,-1);
    		for(int i=1;i<=m;i++){
    			int a,b,c;
    			scanf("%d %d %d",&a,&b,&c);
    			addl(a,b,c);
    			addl(b,a,0); //*
    		}
    		printf("%d
    ",Dinic());
    	}
    	return 0;
    }

     最小费用最大流 

    spfa

            反边的流量是0,花费是相反数。 

    #include<bits/stdc++.h>
    #define ll long long
    #define MOD 998244353 
    #define INF 0x3f3f3f3f
    #define mem(a,x) memset(a,x,sizeof(a))  
    #define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    using namespace std;
    
    const int maxn = 100010;
    
    bool vis[maxn];
    int n,m,s,t;
    int dis[maxn],pre[maxn],last[maxn],flow[maxn],maxflow,mincost;
    
    struct node{
        int to,nxt,flow,dis;  //flow流量 dis花费 
    }edge[maxn];
    int head[maxn],cnt=0;
    
    void addl(int u,int v,int flow,int dis)
    {
        edge[cnt].to=v;
        edge[cnt].flow=flow;
        edge[cnt].dis=dis;
        edge[cnt].nxt=head[u];
        head[u]=cnt++;
    }
    
    bool spfa(int s,int t)
    {
        mem(dis,INF);
        mem(flow,INF);
        mem(vis,0);
        queue<int>q;
        q.push(s);
        vis[s]=1;
        dis[s]=0;
        pre[t]=-1;
        while(!q.empty()){
            int now=q.front();
            q.pop();
            vis[now]=0;
            for(int i=head[now];i!=-1;i=edge[i].nxt)
            {
                if(edge[i].flow>0&&dis[edge[i].to]>dis[now]+edge[i].dis)
                {
                    dis[edge[i].to]=dis[now]+edge[i].dis;
                    pre[edge[i].to]=now;
                    last[edge[i].to]=i;
                    flow[edge[i].to]=min(flow[now],edge[i].flow);
                    if(!vis[edge[i].to])
                    {
                        vis[edge[i].to]=1;
                        q.push(edge[i].to);
                    }
                }
            }
        }
        return pre[t]!=-1;
    }
    void MCMF()
    {
        while(spfa(s,t))
        {
            int now=t;
            maxflow+=flow[t];
            mincost+=flow[t]*dis[t];
            while(now!=s)
            {
                edge[last[now]].flow-=flow[t];
                edge[last[now]^1].flow+=flow[t];
                now=pre[now];
            }
        }
    }
    int main()
    {
        mem(head,-1);
        mem(pre,-1);
        cnt=0;
        maxflow=0;
        mincost=0;
        scanf("%d %d %d %d",&n,&m,&s,&t);
        for(int i=1;i<=m;i++){
            int a,b,c,d;
            scanf("%d %d %d %d",&a,&b,&c,&d);
            addl(a,b,c,d);
            addl(b,a,0,-d);  //反边的流量是0,花费是相反数
        }
        MCMF();
        printf("%d %d",maxflow,mincost);
        return 0;
    }

     例题

    1、Going Home POJ - 2195

          题目大意:一幅图上有若干个人和房子,房子和人的数量相等,每个格子的距离是1,问你所有人回到任意一个房子且没有相同房子时,最少的花费是多少。

          解题思路:建立一个源点与每个人相连,花费为0,流量为1,把每个人和每个房子都连接,边的权值(花费)就是距离,流量设为1,建立一个汇点,把每个房子与汇点连接,花费为0,流量为1,跑一遍最小花费最大流即可。

          代码:

    #include <iostream>
    #include <cstdio>
    #include <fstream>
    #include <algorithm>
    #include <cmath>
    #include <deque>
    #include <vector>
    #include <queue>
    #include <string>
    #include <cstring>
    #include <map>
    #include <stack>
    #include <set>
    #define ll long long
    #define ull unsigned long long 
    #define MOD 998244353 
    #define INF 0x3f3f3f3f
    #define mem(a,x) memset(a,x,sizeof(a))  
    #define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    using namespace std;
    
    const int maxn = 100010;
    
    bool vis[maxn];
    int n,m,s,t;
    int dis[maxn],pre[maxn],last[maxn],flow[maxn],maxflow,mincost;
    
    struct node{
        int to,nxt,flow,dis;  //flow流量 dis花费 
    }edge[maxn];
    int head[maxn],cnt=0;
    
    void addl(int u,int v,int flow,int dis)
    {
        edge[cnt].to=v;
        edge[cnt].flow=flow;
        edge[cnt].dis=dis;
        edge[cnt].nxt=head[u];
        head[u]=cnt++;
    }
    
    bool spfa(int s,int t)
    {
        mem(dis,INF);
        mem(flow,INF);
        mem(vis,0);
        queue<int>q;
        q.push(s);
        vis[s]=1;
        dis[s]=0;
        pre[t]=-1;
        while(!q.empty()){
            int now=q.front();
            q.pop();
            vis[now]=0;
            for(int i=head[now];i!=-1;i=edge[i].nxt)
            {
                if(edge[i].flow>0&&dis[edge[i].to]>dis[now]+edge[i].dis)
                {
                    dis[edge[i].to]=dis[now]+edge[i].dis;
                    pre[edge[i].to]=now;
                    last[edge[i].to]=i;
                    flow[edge[i].to]=min(flow[now],edge[i].flow);
                    if(!vis[edge[i].to])
                    {
                        vis[edge[i].to]=1;
                        q.push(edge[i].to);
                    }
                }
            }
        }
        return pre[t]!=-1;
    }
    void MCMF()
    {
        while(spfa(s,t))
        {
            int now=t;
            maxflow+=flow[t];
            mincost+=flow[t]*dis[t];
            while(now!=s)
            {
                edge[last[now]].flow-=flow[t];
                edge[last[now]^1].flow+=flow[t];
                now=pre[now];
            }
        }
    }
    struct man1{
       int x,y;
    }man[105];
    struct home1{
       int x,y;
    }home[105];
    int main()
    {
        int cc,kk;
        while(~scanf("%d %d",&cc,&kk)){
          getchar();
          mem(head,-1);
          mem(pre,-1);
          if(cc==0)break;
        cnt=0;
        maxflow=0;
        mincost=0;
        int num1=0,num2=0;  
        char c;
        for(int i=1;i<=cc;i++){
          for(int j=1;j<=kk;j++){
             scanf("%c",&c);
             if(c=='m'){
                man[++num1].x=i;
                man[num1].y=j;
             }else if(c=='H'){
                home[++num2].x=i;
                home[num2].y=j;
             }
          }
          getchar();
        }
        int p=num1;
        s=0;
        t=2*p+1;
        for(int i=1;i<=p;i++){
           addl(0,i,1,0);
           addl(i,0,0,0);
           addl(p+i,2*p+1,1,0);
           addl(2*p+1,p+i,0,0);
           for(int j=1;j<=p;j++){
              int d=abs(man[i].x-home[j].x)+abs(man[i].y-home[j].y);
              //cout<<i<<" "<<j+p<<" "<<d<<endl;
              addl(i,j+p,1,d);
              addl(j+p,i,0,-d);
           }
        }
        MCMF();
        cout<<mincost<<endl;
      }
        return 0;
    }

    2、Dining   POJ - 3281

         题目大意:有N头牛 ,F种食物,D种饮料,给你每头牛喜欢的食物和饮料,问你最多可以满足几头牛的需要。

         解题思路:难点即建图。建图时需要把牛拆开,把一头牛分为牛左 和 牛右,首先建立一个源点,从原点引出向食物的有向边,流量为1,在从食物引出向牛左的有向边,也就是与喜欢这种食物的牛相连,流量为1,然后再从牛左引出向牛右的有向边,流量为1,再从牛右引出向饮料的有向边,流量为1,最后将饮料引入汇点即可,最后跑一边最大流即可。

         代码:

    #include <iostream>
    #include <cstdio>
    #include <fstream>
    #include <algorithm>
    #include <cmath>
    #include <deque>
    #include <vector>
    #include <queue>
    #include <string>
    #include <cstring>
    #include <map>
    #include <stack>
    #include <set>
    #define ll long long
    #define MOD 998244353 
    #define INF 0x3f3f3f3f
    #define mem(a,x) memset(a,x,sizeof(a))  
    #define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    using namespace std;
    
    const int maxn= 2000 + 5;  //点
    const int maxm= 2000 + 5;  //边
    int n,m,cnt=0;
    int head[maxn];
    int dis[maxn];
    struct node{
        int e,v,nxt;
    }edge[maxm<<1];
    
    inline void addl(int u,int v,int w)
    {
        edge[cnt].e=v;
        edge[cnt].v=w;
        edge[cnt].nxt=head[u];
        head[u]=cnt++;
    }
    bool bfs()
    {
        mem(dis,-1);
        dis[0]=0;
        queue<int>q;
        q.push(0);
        while(!q.empty()){
            int r=q.front();
            q.pop();
            for(int i=head[r];i!=-1;i=edge[i].nxt)
            {
                int j=edge[i].e;
                if(dis[j]==-1&&edge[i].v)
                {
                    dis[j]=dis[r]+1;
                    q.push(j);
                }
            }
        }
        return dis[n]!=-1;
    }
    int dfs(int u,int flo)
    {
        if(u==n)return flo;
        int detla=flo;
        for(int i=head[u];i!=-1;i=edge[i].nxt)
        {
            int v=edge[i].e;
            if(dis[v]==(dis[u]+1)&&edge[i].v>0)
            {
                int d=dfs(v,min(detla,edge[i].v));
                edge[i].v-=d;
                edge[i^1].v+=d;
                detla-=d;
                if(detla==0)break;
            }
        }
        return flo-detla;
    }
    int Dinic()
    {
        int ans=0;
        while(bfs())
        {
            ans+=dfs(0,INF);
        }
        return ans;
    }
    int main()
    {
        mem(head,-1);
        cnt=0;
        n=500;
        int N,F,D;
        scanf("%d %d %d",&N,&F,&D);
        for(int i=1;i<=F;i++){
            addl(0,i+200,1);
            addl(i+200,0,0);
        }
        for(int i=1;i<=D;i++){
            addl(i+300,500,1);
            addl(500,i+300,0);
        }
        for(int i=1;i<=N;i++){
            int a,b,c;
            scanf("%d %d",&a,&b);
            for(int j=1;j<=a;j++){
                scanf("%d",&c);
                addl(c+200,i,1);
                addl(i,c+200,0);
            }
            addl(i,i+100,1);
            addl(i+100,i,0);
            for(int j=1;j<=b;j++){
                scanf("%d",&c);
                addl(i+100,c+300,1);
                addl(c+300,i+100,0);
            }
        }
        int num=Dinic();
        cout<<num<<endl;
        return 0;
    }
    越自律,越自由
  • 相关阅读:
    Java Stax操作XML简介
    使用JAXB来实现Java合xml之间的转换
    WebService学习笔记系列(四)
    JavaWeb学习笔记总结 目录篇
    成为谷歌的java程序员首先要做到这五点!
    Java实现快速排序
    二叉树遍历(Java实现)
    Java单链表反转
    学好java,做好工程师必读的15本书
    最全前端资源汇集
  • 原文地址:https://www.cnblogs.com/ha-chuochuo/p/13435557.html
Copyright © 2020-2023  润新知