zoukankan      html  css  js  c++  java
  • 最大流问题

    一个小知识:重启控制台输入

    在调试过程中,需要system("pause")一下,如果freopen了in.txt会发现无法pause。这需要重新启用控制台输入。

    • windows下:freopen("CON", "r", stdin)
    • linux下:freopen("/dev/console", "r", stdin)

    Ford-Fulkerson算法:朴素思路

    一开始,每条路径可用流量值都是题目输入的流量。
    每次随机从源点走到终点,在这条路径上用瓶颈流量来更新这条路上所有边的可用流量值。
    如此迭代,直到从源点无法到达终点。

    Ford-Fulkerson算法是一种原理,它的基础是“最大流最小割”定理。
    最大流最小割定理其实就是木桶原理、链条原理。链条中最薄弱的一环决定了链条的强度。
    最大流最小割定理:一个s-t流的最大值,等于其s-t割的最小容量。

    一个定理

    对于一个网络流,如果每条边的最大流量值都是整数,那么整个网络的最大流量值为整数,并且每条边的实际流量也是整数。
    这可以根据Ford-Fulkerson算法的求解过程推理而得,因为每次更新的都是一个整数值。

    Edmond-Karp算法(最短增广路算法)

    使用广搜算法,每次选取源点到终点的最短路径作为增广路。
    复杂度分析:需要进行N(边数)次迭代,每次迭代都需要找到一条增广路,找一条增广路需要进行一次广搜,一次广搜的最差复杂度为O(N×M),即边数乘以点数。总的复杂度为O(N×M×N),即:边数×边数×点数
    如下图,当终结点所在深度固定时,每次广搜都必须遍历全部结点,这是有最坏时间复杂度。并且,每次只能删除掉一条最小边(故需删除M(边数)次)

    EK算法的最佳情况

    Dicnic算法(连续最短增广路算法)

    深搜广搜相结合
    Dicnic算法就是为了解决EK算法的最差情况而生的,然而对于EK算法的最佳情况,Dicnic显得多此一举。
    Dinic算法的思想也是分阶段地在层次网络中增广。
    它与最短增广路算法不同之处是:最短增广路每个阶段执行完一次BFS增广后,要重新启动BFS从源点Vs开始寻找另一条增广路;而在Dinic算法中,只需一次DFS过程就可以实现多次增广,这是Dinic算法的巧妙之处。
    进行完一次BFS之后,终结点的深度就确定了,假设为k。使用DFS可以找到深度为k的全部增广路。每进行一次BFS,都会使得终结点深度变得更深。

    Dicnic算法步骤

    1. 初始化容量网络和网络流
    2. 构造残留网络和层次网络,若汇点不在层次网络中则算法结束 输出最大流
    3. 在层次网络中用一次DFS进行增广,DFS执行完毕,该阶段的增广也就完毕了。
    4. 转至步骤2

    其它算法

    SAP算法、ISAP算法

    一道裸题:hdu1532

    本题是最大流问题最原始的形式 给定M个结点,N条有向边
    此题需要注意

    • 多个样例,注意初始化数据,每次接受新输入之前清空旧数据
    • 有重边,这要求添加边时不能直接赋值,而要用+=

    EK算法

    #include<iostream>
    #include<stdio.h> 
    #include<list>
    #include<string.h>
    #include<queue>
    #include<algorithm>
    using namespace std;
    int N, M;
    const int maxn = 207;
    list<int>g[maxn];
    int flow[maxn][maxn];
    int pre[maxn];
    void bfs(){
    	memset(pre, -1, sizeof(pre));
    	queue<int>q;
    	q.push(1);
    	pre[1] = 0;
    	while (q.empty() == false){
    		int now = q.front();
    		q.pop();
    		for (list<int>::iterator i = g[now].begin(); i != g[now].end(); i++){
    			int to = *i;
    			if (pre[to] == -1 && flow[now][to] > 0){
    				q.push(to);
    				pre[to] = now;
                                    if(to==M)return;//如果找到了终结点,直接返回
    			}
    		}
    	}
    }
    int getMin(){
    	int ans = 1e9;
    	for (int i = M; i != 1; i = pre[i]){
    		ans = min(ans, flow[pre[i]][i]);
    	}
    	return ans;
    }
    void update(int mi){
    	for (int i = M; i != 1; i = pre[i]){
    		flow[pre[i]][i] -= mi;
    	}
    }
    int main(){
    	freopen("in.txt", "r", stdin);
    	while (cin >> N >> M){
    		for (int i = 1; i <= M; i++)g[i].clear();
    		memset(flow, 0, sizeof(flow));
    		for (int i = 0; i < N; i++){
    			int from, to, f;
    			cin >> from >> to >> f;
    			flow[from][to] += f;
    			g[from].push_back(to);
    		}
    		int ans = 0;
    		while (true){
    			bfs();
    			if (pre[M] == -1)break;
    			int mi = getMin();
    			update(mi);
    			ans += mi;
    		}
    		cout << ans << endl;
    	}
    }
    

    网上有此题代码,这份代码直接进行深搜,相当于dicnic算法没有BFS阶段,效率不知如何

    #include <cstdio>  
    #include <cstring>  
    #include <iostream>  
    #include <string>  
    #include <algorithm>  
    #include <map>  
    #include <vector>  
    using namespace std;  
    const int N = 1100;  
    const int INF = 0x3f3f3f3f;  
      
    struct Node  
    {  
        int to;//终点  
        int cap; //容量  
        int rev;  //反向边  
    };  
      
    vector<Node> v[N];  
    bool used[N];  
      
    void add_Node(int from,int to,int cap)  //重边情况不影响  
    {  
        v[from].push_back((Node){to,cap,v[to].size()});  
        v[to].push_back((Node){from,0,v[from].size()-1});  
    }  
      
    int dfs(int s,int t,int f)  
    {  
        if(s==t)  
            return f;  
        used[s]=true;  
        for(int i=0;i<v[s].size();i++)  
        {  
            Node &tmp = v[s][i];  //注意  
            if(used[tmp.to]==false && tmp.cap>0)  
            {  
                int d=dfs(tmp.to,t,min(f,tmp.cap));  
                if(d>0)  
                {  
                    tmp.cap-=d;  
                    v[tmp.to][tmp.rev].cap+=d;  
                    return d;  
                }  
            }  
        }  
        return 0;  
    }  
      
    int max_flow(int s,int t)  
    {  
        int flow=0;  
        for(;;){  
            memset(used,false,sizeof(used));  
            int f=dfs(s,t,INF);  
            if(f==0)  
                return flow;  
            flow+=f;  
        }  
    }  
    int main()  
    {  
        int n,m;  
        while(~scanf("%d%d",&n,&m))  
        {  
            memset(v,0,sizeof(v));  
            for(int i=0;i<n;i++)  
            {  
                int x,y,z;  
                scanf("%d%d%d",&x,&y,&z);  
                add_Node(x,y,z);  
            }  
            printf("%d
    ",max_flow(1,m));  
        }  
    }  
    

    Dicnic算法

    #include <stdio.h>  
    #include <string.h>  
    #define VM 2000  
    #define EM 205500  
    #define inf 0x3f3f3f3f  
    struct Edge  
    {  
        int frm,to,cap,next;  
    }edge[EM];  
      
    int head[VM],dep[VM],ep;     //dep为点的层次  
    void addedge (int cu,int cv,int cw)  //第一条边下标必须为偶数  
    {  
        edge[ep].frm = cu;  
        edge[ep].to = cv;  
        edge[ep].cap = cw;  
        edge[ep].next = head[cu];  
        head[cu] = ep;  
        ep ++;  
        edge[ep].frm = cv;  
        edge[ep].to = cu;  
        edge[ep].cap = 0;  
        edge[ep].next = head[cv];  
        head[cv] = ep;  
        ep ++;  
    }  
      
    int BFS (int src,int des)     //求出层次图  
    {  
        int que[VM],i,front = 0,rear = 0;  
        memset (dep,-1,sizeof(dep));  
        que[rear++] = src;  
        dep[src] = 0;  
        while (front != rear)  
        {  
            int u = que[front++];  
            front = front%VM;  
            for (i = head[u];i != -1;i = edge[i].next)  
            {  
                int v = edge[i].to;  
                if (edge[i].cap > 0&&dep[v] == -1) //容量大于0&&未在dep中  
                {  
                    dep[v] = dep[u] + 1;        //建立层次图  
                    que[rear ++] = v;  
                    rear = rear % VM;  
                    if (v == des)  //找到汇点 返回  
                        return 1;  
                }  
            }  
        }  
        return 0;  
    }  
    int dinic (int src,int des)  
    {  
        int i,res = 0,top;  
        int stack[VM];    //stack为栈,存储当前增广路  
        int cur[VM];        //存储当前点的后继 跟head是一样的  
        while (BFS(src,des))   //if BFS找到增广路  
        {  
            memcpy (cur,head,sizeof (head));  
            int u = src;       //u为当前结点  
            top = 0;  
            while (1)  
            {  
                if (u == des)     //增广路已全部进栈  
                {  
                    int min = inf,loc ;  
                    for (i = 0;i < top;i ++)       //找最小的增广跟并loc记录其在stack中位置  
                        if (min > edge[stack[i]].cap)  //以便退回该边继续DFS  
                        {  
                            min = edge[stack[i]].cap;  
                            loc = i;  
                        }  
                    for (i = 0;i < top;i ++)   //偶数^1 相当加1 奇数^1相当减1 当正向边 = 0&&路径不合适时,正加负减  
                    {                           //偶数是正向边,奇数是负向边,边从0开始  
                        edge[stack[i]].cap -= min;  
                        edge[stack[i]^1].cap += min;  
                    }                              //将增广路中的所有边修改  
                    res += min;  
                    top = loc;  
                    u = edge[stack[top]].frm;         //当前结点修改为最小边的起点  
                }  
                for (i = cur[u];i != -1;cur[u] = i = edge[i].next)   //找到当前结点对应的下一条边  
                    if (edge[i].cap != 0&&dep[u] + 1 == dep[edge[i].to])//不满足条件时,修改cur值(去掉不合适的占)eg:1-->2 1-->3 1-->4 有边 但只有  
                        break;                                  // 1-->4 这条边满足条件 就把1到2、3的边给去掉  
                if (cur[u] != -1)            //当前结点的下一条边存在  
                {  
                    stack[top ++] = cur[u];   //把该边放入栈中  
                    u = edge[cur[u]].to;         //再从下个点开始找  
                }  
                else  
                {  
                    if (top == 0)        //当前结点无未遍历的下一条边且栈空,DFS找不到下一条增广路  
                        break;  
                    dep[u] = -1;            //当前结点不在增广路中,剔除该点  
                    u = edge[stack[--top]].frm; //退栈 回朔,继续查找  
                }  
            }  
        }  
        return res;  
    }  
    int main ()///坐标从0或1开始均可  注意别忘记下面的2个初始化  
    {  
        int np,nc,m,v1,v2,w,n;  
        int src,des;  
        char str[20];  
        while (scanf ("%d%d%d%d",&n,&np,&nc,&m)!=EOF)  
        {  
            ep = 0;//边的初始化  
            src = n;  
            des = n+1;  
            memset (head,-1,sizeof(head));///这里初始化  
            while (m --)  
            {  
                scanf ("%s",str);  
                sscanf (str,"(%d,%d)%d",&v1,&v2,&w);  
                addedge (v1,v2,w);  
            }  
            while (np --)  
            {  
                scanf ("%s",str);  
                sscanf (str,"(%d)%d",&v2,&w);  
                addedge (src,v2,w);  
            }  
            while (nc--)  
            {  
                scanf ("%s",str);  
                sscanf (str,"(%d)%d",&v1,&w);  
                addedge (v1,des,w);  
            }  
            int ans = dinic (src,des);  
            printf ("%d
    ",ans);  
        }  
        return 0;  
    }  
    

    SAP算法

    #include <stdio.h>  
    #include <string.h>  
    const int VM = 110, EM = 20500, inf = 0x3f3f3f3f;  
    struct Edge  
    {  
        int to, frm, nxt, cap;  
    }edge[EM];  
      
    int head[VM], ep, n, src, des;  
    int dep[VM], gap[VM]; //gap[x]=y:说明残留网络中dep[i]=x的个数为y  
      
    void addedge(int u, int v, int c)  
    {  
        edge[ep].frm = u;  
        edge[ep].to = v;  
        edge[ep].cap = c;  
        edge[ep].nxt = head[u];  
        head[u] = ep++;  
        edge[ep].frm = v;  
        edge[ep].to = u;  
        edge[ep].cap = 0;  
        edge[ep].nxt = head[v];  
        head[v] = ep++;  
    }  
      
    void BFS()  
    {  
        memset(dep, -1, sizeof(dep));  
        memset(gap, 0, sizeof(gap));  
        gap[0] = 1;   //说明此时有1个dep[i] = 0  
        int que[VM], front = 0, rear = 0;  
        dep[des] = 0;  
        que[rear++] = des;  
        int u, v;  
        while (front != rear)  
        {  
            u = que[front++];  
            front = front%VM;  
            for (int i=head[u]; i!=-1; i=edge[i].nxt)  
            {  
                v = edge[i].to;  
                if (edge[i].cap != 0 || dep[v] != -1)  
                    continue;  
                que[rear++] = v;  
                rear = rear % VM;  
                ++gap[dep[v] = dep[u] + 1];  //求出各层次的数量  
            }  
        }  
    }  
      
    int Sap()  
    {  
        int res = 0;  
        BFS();  
        int cur[VM];  
        int stack[VM], top = 0;  
        memcpy(cur, head, sizeof(head));  
        int u = src, i;  
        while (dep[src] < n)  
        {  
            if (u == des)  
            {  
                int temp = inf, inser = n;  
                for (i=0; i!=top; ++i)  
                    if (temp > edge[stack[i]].cap)  
                    {  
                        temp = edge[stack[i]].cap;  
                        inser = i;  
                    }  
                for (i=0; i!=top; ++i)  
                {  
                    edge[stack[i]].cap -= temp;  
                    edge[stack[i]^1].cap += temp;  
                }  
                res += temp;  
                top = inser;  
                u = edge[stack[top]].frm;  
            }  
      
            if (dep[u] != 0 && gap[dep[u] -1] == 0)//出现断层,无增广路  
                break;  
            for (i = cur[u]; i != -1; i = edge[i].nxt)//遍历与u相连的未遍历结点  
                if (dep[edge[i].to] != -1)  
                    if (edge[i].cap != 0 && dep[u] == dep[edge[i].to] + 1) //层序关系, 找到允许  
                        break;  
      
            if (i != -1)//找到允许弧  
            {  
                cur[u] = i;  
                stack[top++] = i;//加入路径栈  
                u = edge[i].to;//查找下一个结点  
            }  
            else   //无允许的路径,修改标号 当前点的标号比与之相连的点中最小的多1  
            {  
                int min = n;  
                for (i = head[u]; i != -1; i = edge[i].nxt) //找到与u相连的v中dep[v]最小的点  
                {  
                    if (edge[i].cap == 0)  
                        continue;  
                    if (min > dep[edge[i].to])  
                    {  
                        min = dep[edge[i].to];  
                        cur[u] = i;          //最小标号就是最新的允许弧  
                    }  
                }  
                --gap[dep[u]];          //dep[u] 的个数变化了 所以修改gap  
                ++gap[dep[u] = min + 1]; //将dep[u]设为min(dep[v]) + 1, 同时修改相应的gap[]  
                if (u != src) //该点非源点&&以u开始的允许弧不存在,退点  
                    u = edge[stack[--top]].frm;  
            }  
        }  
        return res;  
    }  
      
    int main()///坐标从0开始  
    {  
        int i, np, nc, m;  
        char str[10];  
        while (scanf("%d %d %d %d", &n, &np, &nc, &m) != EOF)  
        {  
            ep = 0;  
            memset(head, -1, sizeof(head));  
            int u, v, c;  
            src = n, des = n + 1;  
            n += 2;  
            for (i=0; i!=m; ++i)  
            {  
                scanf("%stack", str);  
                sscanf(str, "(%d,%d)%d", &u, &v, &c);  
                addedge(u, v, c);  
            }  
            for (i=0; i!=np; ++i)  
            {  
                scanf("%stack", str);  
                sscanf(str, "(%d)%d", &v, &c);  
                addedge(src, v, c);  
            }  
            for (i=0; i!=nc; ++i)  
            {  
                scanf("%stack", str);  
                sscanf(str, "(%d)%d", &u, &c);  
                addedge(u, des, c);  
            }  
            printf("%d
    ", Sap());  
        }  
        return 0;  
    }  
    
  • 相关阅读:
    (4)Maven快速入门_4在Spring+SpringMVC+MyBatis+Oracle+Maven框架整合运行在Tomcat8中
    (3)Maven快速入门_3在Eclipse中创建Maven项目打包成jar
    (2)Maven快速入门_2maven在Eclipse中的设置
    (1)Maven快速入门_1maven安装
    (11)Microsoft office Word 2013版本操作入门_word中表格操作
    (10)Microsoft office Word 2013版本操作入门_word表格
    洛谷 P2144 [FJOI2007]轮状病毒
    洛谷 P2234 [HNOI2002]营业额统计
    【模板】主席树
    洛谷 P2572 [SCOI2010]序列操作
  • 原文地址:https://www.cnblogs.com/weiyinfu/p/6513963.html
Copyright © 2011-2022 走看看