• 最大流


    基本知识:  流网络G = (V,E)是一个有向图,其中每条边有一非负容量c(u,v) >= 0。如果(u,v) 不属于E,则假定c(u,v) = 0。

           两个特殊点:源点:S  汇点: T              

             c:容量函数   f:实值函数(也就是最大流)

    三个性质:   容量限制:对所有边,f(u,v) <= c(u,v);

          反对称性:对所有边,f(u,v) = - f(u,v);

          流守恒性:对除源点和汇点外的任意点,该点的流的为0。该点的流记为:|f|。

    三种重要思想:  残留网络:Gf = (V,Ef) ,  其中边(u,v)的残留流量cf(u,v) = c(u,v) - f(u,v)  即可以再增加的流量值。

                 Ef的建立:if 边(u,v) 属于 E

                        if f(u,v) < c(u,v)

                         then cf(u,v) = c(u,v) - f(u,v) > 0

                        if   f(u,v) > 0    so  f(v,u) < 0

                         then cf(v,u) = c(v,u) - f(v,u) > 0

                      else if 边(u,v)和(v,u)都不属于E   so c(u,v) = c(v,u) = 0,f(u,v) = f(v,u) = 0

                         then cf(u,v) = cf(v,u) = c(u,v) - f(u,v) = c(v,u) - f(v,u) = 0

                      SO   cf(u,v) = c(u,v) - f(u,v)

            增广路径:在Gf中一条从s到t的简单路径。最大流算法就是求增广路径,优化也是对这个进行优化。

            流网络的割G中割(S,T)将V划分成S和T=E-S两部分,使得s属于S,t属于T。

                  f(S,T):穿过割(S,T)的净流,包括从S到T的流量,也包括从T到S的流量

                  c(S,T):割(S,T)的容量,只包括从S到T的容量

                  对所有割,其f都是相同的,但c是有大小的

                  最小割:G中所有割中具有最小容量的割

    最大流最小割定理:f是具有s和t的G中的一个流,下面三个定理等价:

             1:f是G的一个最大流

             2:Gf不包含增广路径

             3:对G中的某个割,有|f| = c(S,T)

    Ford-Fulkerson方  为什么叫Ford-Fulkerson方法而不是算法,原因在于可以用多种方式实现这一方法,方式并不唯一。

               在每次迭代中,找出任意增广路径p,并把沿p每条边的流f加上其残留容留cf(p)。

     

    Edmonds-Karp算法基于广度优先搜索(BFS)来计算增广路径P  时间复杂度O(V*E^2)

               算法流程如下:

                设队列Q--存储当前未检查的标号点,队首节点出队后,成为已检查的标点;

                path -- 存储当前已标号可改进路经;

                repeat

                       path置空;

                      源点s标号并进入path和Q;

                       while  Q非空  and  汇点t未标号 do

                              begin

                                     移出Q的队首顶点u;

                                     for 每一条从u出发的弧(u,v) do

                                            if  v未标号 and 弧(u,v)的流量可改进

                            then v进入队列Q和path;

                          end while

                        if 汇点已标号

                      then 从汇点出发沿着path修正可改进路的流量;

                  until 汇点未标号;

       

      Edmonds-Karp算法有一个很重要的性质:当汇点未标号而导致算法结束的时候,那些已经标号的节点构成集合S,未标号的节点构成集合T,割(S,T)恰好是该流网络的最小割;且这样求出的最小割(S,T)中集合S的元素数目一定是最少的。

      BFS遍历增广路径代码:

    HDU:1532 http://acm.hdu.edu.cn/showproblem.php?pid=1532

    代码
    #include <iostream>
    #include
    <queue>
    using namespace std;

    #define min(a,b) (a>b?b:a)

    int n,m; //n条边,m个点
    int Start,End;
    queue
    <int> Q;
    const long maxn = 205;
    const long inf = 1000000000;

    int net[maxn][maxn]; //残留网络
    int path[maxn]; //增广路径
    int flow[maxn]; //增广路径的通过容量

    void Init()
    {
    int i;
    int from,to,val;

    memset(net,
    0,sizeof(net));

    for(i=0;i<n;i++)
    {
    scanf(
    "%d%d%d",&from,&to,&val);
    net[from][to]
    += val;
    }

    Start
    = 1;
    End
    = m;
    //scanf("%d%d",&Start,&End);
    }

    int bfs()
    {
    //寻找增广路径
    while(!Q.empty())
    {
    Q.pop();
    }
    memset(path,
    -1,sizeof(path));

    path[Start]
    = 0;
    flow[Start]
    = inf;

    Q.push(Start);
    while(!Q.empty())
    {
    int top = Q.front();
    Q.pop();

    if(top == End)
    break;

    int i;
    for(i=1;i<=m;i++)
    {
    if(i != Start && -1 == path[i] && net[top][i])
    {
    flow[i]
    = min(flow[top],net[top][i]);
    Q.push(i);
    path[i]
    = top;
    }
    }
    }

    if(-1 == path[End])
    return -1;

    return flow[m]; //为什么是m,不是End
    }

    void print(int ans)
    {
    printf(
    "%d\n",ans);
    }

    void Edmonds_Karp()
    {
    int max_flow = 0;

    int step;

    while((step=bfs()) != -1)//不停的找增广路径,至到找不到
    {
    max_flow
    += step;

    /*对残留网络进行修改*/
    int now = End;
    while(now != Start)
    {
    int pre = path[now];
    net[pre][now]
    -= step;
    net[now][pre]
    += step;
    now
    = pre;
    }
    }

    print(max_flow);
    }

    int main()
    {
    while(scanf("%d%d",&n,&m)!=EOF)
    {
    Init();
    Edmonds_Karp();
    }
    return 0;
    }


    Dinic算法:

     
     

    自己对着网上写的一个代码,还很挫,用空再改改:

    代码
    #include <iostream>
    #include
    <queue>
    #include
    <stack>
    using namespace std;

    const long maxn = 205;
    const long inf = 1000000000;

    #define max(a,b) (a>b?a:b)
    #define min(a,b) (a<b?a:b)

    int m,n;
    int start,end;
    int map[maxn][maxn];
    int level[maxn];
    int vis[maxn];

    void Init()
    {
    int i,a,b,c;
    memset(map,
    0,sizeof(map));
    for(i=0;i<m;i++)
    {
    scanf(
    "%d%d%d",&a,&b,&c);
    map[a][b]
    += c;
    }
    start
    = 1;
    end
    = n;
    }

    bool bfs()
    {
    int i;
    memset(level,
    -1,sizeof(level));

    queue
    <int> Q;
    Q.push(start);
    level[start]
    = 0;

    while(!Q.empty())
    {
    int top = Q.front();
    Q.pop();

    for(i=1;i<=n;i++)
    {
    if(level[i] == -1 && map[top][i])
    {
    level[i]
    = level[top] + 1;
    Q.push(i);
    }
    }
    }

    if(level[end] == -1)
    return false;

    return true;
    }

    int dfs(int ss,int limit)
    {
    int i;
    if(ss == end)
    return limit;

    vis[ss]
    = 1;

    int flow = 0;
    for(i=1;i<=n;i++)
    {
    if(level[i] - level[ss] == 1 && map[ss][i] && vis[i] == 0)
    {
    int temp = dfs(i,min(limit,map[ss][i]));
    limit
    -= temp;

    map[ss][i]
    -= temp;
    map[i][ss]
    += temp;

    flow
    += temp;

    if(limit == 0)
    break;
    }
    }
    vis[ss]
    = 0;

    return flow;
    }

    int Dinic()
    {
    int max_flow = 0;
    while(bfs())
    {
    memset(vis,
    0,sizeof(vis));
    int flow = dfs(start,inf);
    if(flow == 0)
    break;
    max_flow
    += flow;
    }
    return max_flow;
    }
    int main()
    {
    while(scanf("%d%d",&m,&n)!=EOF)
    {
    Init();
    int num = Dinic();

    printf(
    "%d\n",num);
    }
    return 0;
    }

    用邻接表写的

    代码
    #include <iostream>
    using namespace std;

    const long inf = 1000000000;
    const long maxn_point = 205;
    const long maxn_edge = 20005;

    inline fmin(
    int a,int b){return a<b?a:b;}
    inline fmax(
    int a,int b){return a>b?a:b;}

    int n,m;//m条边,n个点
    int start,end;
    int Index;
    struct node
    {
    int to;
    int val;
    int next;
    }ditch[maxn_edge];
    int pre[maxn_point];
    int level[maxn_point],queue[maxn_point]; //层次图,模拟队列

    inline
    void single_insert(int from,int to,int cap)
    {
    ditch[Index].to
    = to;
    ditch[Index].val
    = cap;
    ditch[Index].next
    = pre[from];
    pre[from]
    = Index++;
    }
    void insert(int from,int to,int cap)
    {
    single_insert(from,to,cap);
    single_insert(to,from,
    0);
    }

    void Init()
    {
    int a,b,c;
    Index
    = 0;
    memset(pre,
    -1,sizeof(pre));

    while(m--)
    {
    scanf(
    "%d%d%d",&a,&b,&c);
    insert(a,b,c);
    }
    start
    = 1;
    end
    = n;
    }

    bool bfs()
    {
    int i;
    int ed=0,bg=0,x,y;
    memset(level,
    -1,sizeof(level));
    level[start]
    = 0;
    queue[ed
    ++] = start;

    while(bg<ed)
    {
    x
    = queue[bg++];
    for(i=pre[x];i+1;i=ditch[i].next)
    {
    y
    = ditch[i].to;
    if(ditch[i].val && level[y] == -1)
    {
    level[y]
    = level[x] + 1;
    if(y == end)//层次图不必要全部建完
    return true;
    queue[ed
    ++] = y;
    }
    }
    }
    return false;
    }

    int dfs(int x,int limit = inf)
    {
    int i,y,ret;
    if(x == end)
    return limit;

    int flow = 0;
    for(i=pre[x]; (i+1) && limit; i=ditch[i].next)
    {
    y
    = ditch[i].to;
    if(ditch[i].val && level[y] == level[x] + 1)
    {
    ret
    = dfs(y,fmin(limit,ditch[i].val));

    ditch[i].val
    -= ret;
    ditch[i
    ^1].val += ret;
    limit
    -= ret;
    flow
    += ret;
    }
    }
    return flow;
    }

    int Dinic()
    {
    int max_flow = 0;
    while(bfs())
    {
    max_flow
    += dfs(start);
    }

    return max_flow;
    }

    int main()
    {
    while(scanf("%d%d",&m,&n)!=EOF)
    {
    Init();
    int num = Dinic();
    printf(
    "%d\n",num);
    }
    return 0;
    }


    hdu:3526 http://acm.hdu.edu.cn/showproblem.php?pid=3526

    设置超级源点和超级汇点,建立流向图。最大流做法

    代码
    #include <iostream>
    #include
    <queue>
    using namespace std;

    #define fmax(a,b) (a>b?a:b)
    #define fmin(a,b) (a<b?a:b)

    const long maxn_point = 505;
    const long maxn_edge = 500050;
    const long inf = 1e9;


    int n,m,Index;
    int start,end;
    struct node
    {
    int to;
    int val;
    int next;
    }com[maxn_edge];
    int pre[maxn_point];
    int level[maxn_point];

    void _insert(int from,int to,int val)
    {
    com[Index].to
    = to;
    com[Index].val
    = val;
    com[Index].next
    = pre[from];
    pre[from]
    = Index++;
    }
    void insert(int from,int to,int val)
    {
    _insert(from,to,val);
    _insert(to,from,
    0);
    }

    void Init()
    {
    int i,a,b,c;
    Index
    = 0;
    start
    = 0,end = n+1;
    memset(pre,
    -1,sizeof(pre));

    for(i=1;i<=n;i++)
    {
    scanf(
    "%d",&c);
    insert(start,i,c);
    }
    for(i=1;i<=n;i++)
    {
    scanf(
    "%d",&c);
    insert(i,end,c);
    }
    for(i=0;i<m;i++)
    {
    scanf(
    "%d%d%d",&a,&b,&c);
    _insert(a,b,c);
    _insert(b,a,c);
    }
    }

    bool bfs()
    {
    int i;
    memset(level,
    -1,sizeof(level));

    queue
    <int> Q;
    Q.push(start);
    level[start]
    = 0;

    while(!Q.empty())
    {
    int x = Q.front();
    Q.pop();

    for(i=pre[x];(i+1);i=com[i].next)
    {
    int y = com[i].to;
    if(level[y] == -1 && com[i].val)
    {
    level[y]
    = level[x] + 1;
    if(y == end)
    return true;
    Q.push(y);
    }
    }
    }

    return false;
    }

    int dfs(int x,int limit = inf)
    {
    int i;
    if(x == end)
    return limit;

    int flow = 0,temp;
    for(i=pre[x]; (i+1)&&limit; i=com[i].next)
    {
    int y = com[i].to;
    if(com[i].val && level[y] == level[x] + 1)
    {
    temp
    = dfs(y,fmin(limit,com[i].val));

    com[i].val
    -= temp;
    com[i
    ^1].val += temp;

    flow
    += temp;
    limit
    -= temp;
    }
    }

    return flow;
    }

    int dinic()
    {
    int max_flow = 0;
    while(bfs())
    {
    max_flow
    += dfs(start);
    }

    return max_flow;
    }

    int main()
    {
    while(scanf("%d%d",&n,&m)!=EOF)
    {
    Init();
    int num = dinic();
    printf(
    "%d\n",num);
    }
    return 0;
    }

    hdu:2063 http://acm.hdu.edu.cn/showproblem.php?pid=2063

    建图的时候从女生到男生有容量,而从男生到女生那里没有的。

    代码
    #include <iostream>
    #include
    <queue>
    using namespace std;

    #define fmax(a,b) (a>b?a:b)
    #define fmin(a,b) (a<b?a:b)

    const long maxn_point = 1100;
    const long maxn_edge = 10005;
    const long inf = 1e9;

    int Index,k,m,n;
    int start,end;

    struct node
    {
    int to;
    int val;
    int next;
    }people[maxn_edge];
    int pre[maxn_point];
    int level[maxn_point];

    void _insert(int from,int to,int val)
    {
    people[Index].to
    = to;
    people[Index].val
    = val;
    people[Index].next
    = pre[from];
    pre[from]
    = Index++;
    }

    void insert(int from,int to,int val)
    {
    _insert(from,to,val);
    _insert(to,from,
    0);
    }

    void Init()
    {
    int i,a,b;
    Index
    = 0;

    scanf(
    "%d%d",&m,&n);
    start
    = 0,end = m+n+1;

    memset(pre,
    -1,sizeof(pre));

    for(i=1;i<=m;i++)
    {
    insert(start,i,
    1);
    }
    for(i=1;i<=n;i++)
    {
    insert(m
    +i,end,1);
    }
    while(k--)
    {
    scanf(
    "%d%d",&a,&b);
    _insert(a,m
    +b,1);
    _insert(m
    +b,a,0);
    }
    }

    bool bfs()
    {
    int i;
    queue
    <int> Q;

    Q.push(start);
    memset(level,
    -1,sizeof(level));
    level[start]
    = 0;

    while(!Q.empty())
    {
    int x = Q.front();
    Q.pop();

    for(i=pre[x];(i+1);i=people[i].next)
    {
    int y = people[i].to;
    if(people[i].val && -1 == level[y])
    {
    level[y]
    = level[x] + 1;
    if(y == end)
    return true;
    Q.push(y);
    }
    }
    }

    return false;
    }

    int dfs(int x,int limit = inf)
    {
    int i;
    if(x == end)
    return limit;

    int flow = 0,temp;
    for(i=pre[x]; (i+1) && limit; i=people[i].next)
    {
    int y = people[i].to;
    if(people[i].val && level[y] == level[x] + 1 && (temp = dfs(y,fmin(limit,people[i].val))))
    {
    people[i].val
    -= temp;
    people[i
    ^1].val += temp;

    limit
    -= temp;
    flow
    += temp;
    }
    }

    return flow;
    }
    int dinic()
    {
    int max_flow = 0;
    while(bfs())
    {
    //while(flow = dfs(start))
    //max_flow += flow;
    max_flow += dfs(start);
    }

    return max_flow;
    }

    int main()
    {
    while(scanf("%d",&k)!=EOF && k!=0)
    {
    Init();
    int num = dinic();
    printf(
    "%d\n",num);
    }
    return 0;
    }

    SAP算法:从傻崽blog里拉来的。先当模板用!

    邻接矩阵:

    代码
    #define M 1000
    int maze[M][M];
    int gap[M],dis[M],pre[M],cur[M];
    int sap(int s,int t,int nodenum) {
    CC(cur,
    0);CC(dis,0);CC(gap,0);
    int u = pre[s] = s,maxflow = 0,aug = -1;
    gap[
    0] = nodenum;
    while(dis[s] < nodenum) {
    loop: FOR(v,cur[u],nodenum)
    if(maze[u][v] && dis[u] == dis[v] + 1) {
    checkmin(aug,maze[u][v]);
    pre[v]
    = u;
    u
    = cur[u] = v;
    if(v == t) {
    maxflow
    += aug;
    for(u = pre[u];v != s;v = u,u = pre[u]) {
    maze[u][v]
    -= aug;
    maze[v][u]
    += aug;
    }
    aug
    = -1;
    }
    goto loop;
    }
    int mindis= nodenum-1;
    FF(v,nodenum)
    if(maze[u][v] && mindis> dis[v]) {
    cur[u]
    = v;
    mindis
    = dis[v];
    }
    if((--gap[dis[u]])== 0) break;
    gap[dis[u]
    = mindis+1] ++;
    u
    = pre[u];
    }
    return maxflow;
    }

    邻接表:

    代码
    #define M 40010
    int gap[M],dis[M],pre[M],cur[M];
    int NE,NV;
    int head[M];
    struct Node{
    int c,pos,next;
    }E[
    999999];
    int sap(int s,int t) {
    memset(dis,
    0,sizeof(int)*(NV+1));
    memset(gap,
    0,sizeof(int)*(NV+1));
    FF(i,NV) cur[i]
    = head[i];
    int u = pre[s] = s,maxflow = 0,aug = -1;
    gap[
    0] = NV;
    while(dis[s] < NV) {
    loop:
    for(int &i = cur[u]; i != -1; i = E[i].next) {
    int v = E[i].pos;
    if(E[i].c && dis[u] == dis[v] + 1) {
    checkmin(aug,E[i].c);
    pre[v]
    = u;
    u
    = v;
    if(v == t) {
    maxflow
    += aug;
    for(u = pre[u];v != s;v = u,u = pre[u]) {
    E[cur[u]].c
    -= aug;
    E[cur[u]
    ^1].c += aug;
    }
    aug
    = -1;
    }
    goto loop;
    }
    }
    int mindis = NV;
    for(int i = head[u]; i != -1 ; i = E[i].next) {
    int v = E[i].pos;
    if(E[i].c && mindis > dis[v]) {
    cur[u]
    = i;
    mindis
    = dis[v];
    }
    }
    if( (--gap[dis[u]]) == 0) break;
    gap[ dis[u]
    = mindis+1 ] ++;
    u
    = pre[u];
    }
    return maxflow;
    }
    void Insert(int u,int v,int c,int cc = 0) {
    E[NE].c
    = c; E[NE].pos = v;
    E[NE].next
    = head[u]; head[u] = NE++;
    E[NE].c
    = cc; E[NE].pos = u;
    E[NE].next
    = head[v]; head[v] = NE++;
    }

    int main() {
    FF(i,NV) head[i]
    = -1;
    NE
    = 0;
    }
    /**** **** **** **** **** ****
    网络中最小费用最大流
    参数含义: n代表网络中的总节点数
    net[][]代表剩余网络
    cost[][]代表单位费用
    path[]保存增广路径
    ecost[]源点到各点的最短路
    算法:初始最小费用和最大流均为,寻找单位费用最短路
    在最短路中求出最大流,即为增广路,再修改剩余网络,直到无可增广路为止
    返回值: 最小费用,最大流量
    **** **** **** **** **** ***
    */
    const int NMAX = 210;
    int net[NMAX][NMAX], cost[NMAX][NMAX];
    int path[NMAX], ecost[NMAX];
    int n;
    bool bellman_ford()
    {
    int i,j;
    memset(path,
    -1,sizeof(path));
    fill(ecost, ecost
    +NMAX, INT_MAX);
    ecost[
    0] = 0;

    bool flag = true;
    while(flag) {
    flag
    = false;
    for(i=0;i<=n;i++) {
    if(ecost[i] == INT_MAX) {
    continue ;
    }
    for(j=0;j<=n;j++) {
    if(net[i][j] > 0 && ecost[i]+cost[i][j] < ecost[j]) {
    flag
    = true;
    ecost[j]
    = ecost[i]+cost[i][j];
    path[j]
    = i;
    }
    }
    }
    }
    return ecost[n] != INT_MAX;
    }

    int min_cost_max_flow()
    {
    int i,j;
    int mincost = 0, maxflow = 0;
    while( bellman_ford() ) {
    int now = n;
    int neck = INT_MAX;
    while(now != 0) {
    int pre = path[now];
    neck
    = min(neck, net[pre][now]);
    now
    = pre;
    }
    maxflow
    += neck;
    now
    = n;
    while(now != 0) {
    int pre = path[now];
    net[pre][now]
    -= neck;
    net[now][pre]
    += neck;
    cost[now][pre]
    = - cost[pre][now];
    mincost
    += cost[pre][now] * neck;
    now
    = pre;
    }
    }
    return mincost;
    }
  • 相关阅读:
    更新数据库时日期字段为空显示为190011
    VB6封装DLL
    【转】Excel技巧收集
    数据透视表的计算字段
    站点地图与权限:Sitemap and Role
    asp:Menu导航菜单CSS
    单元格太窄的#号
    获得数据库中某个字段值
    第5周作业——如何在eclipse上配置tomcat harara
    第7周作业耦合性 harara
  • 原文地址:https://www.cnblogs.com/silencExplode/p/1912189.html
Copyright © 2020-2023  润新知