##网络流##

HLPP参考:http://blog.sina.com.cn/s/blog_60707c0f01010oqt.html

ISAP参考:http://blog.sina.com.cn/s/blog_6165e6920100ea08.html

参考:http://blog.sina.com.cn/s/blog_476a25110100mag7.html

http://wenku.baidu.com/link?url=Q3y_0x9BaO1L1W5Kr3AJzgCUVJVKNjG8UGw2nt44d6HDWTT3QLWygZ8IT_lb-V6GNjYoV4Fh74bsB88uTMeJA4xWBnbAyNfUr9VRCo-rCFS

//algo1:
//Ford-Fulkerson
//O(n*m*U)
#define N 115
struct arc{
    int c,f;
}e[N][N];
int flag[N],al[N],pre[N];
int n,m,np,nc;
int ford(int s,int t,int n)
{   while (1)
        {   memset(flag,255,sizeof(flag));
            memset(pre,255,sizeof(pre));
            memset(al,255,sizeof(al));
            queue<int > q;
            flag[s]=0; pre[s]=-1; al[s]=inf;
            q.push(s);
            while (!q.empty()&&flag[t]==-1)
                {
                    int v=q.front(); q.pop();
                    for (int i=0;i<n;i++)
                    if (flag[i]==-1)
                        {
                            if (e[v][i].f<e[v][i].c)
                                {
                                    flag[i]=0; pre[i]=v;
                                    al[i]=min(al[v],e[v][i].c-e[v][i].f);
                                    q.push(i);
                                }
                            else if (e[i][v].f>0)
                                {   flag[i]=0; pre[i]=-v;
                                    al[i]=min(al[v],e[i][v].f);
                                    q.push(i);
                                }
                        }
                    flag[v]=1;
                }

            if (flag[t]==-1||al[t]==0) break;
            int k1=t,k2=abs(pre[k1]),alph=al[t];
            while (1)
                {   if (e[k2][k1].c>0)
                         e[k2][k1].f+=alph;
                    else e[k1][k2].f-=alph;
                    if (k2==s) break;
                    k1=k2; k2=abs(pre[k1]);
                }
        }
    int ans=0;
    for (int i=0;i<n;i++)
        ans+=e[s][i].f;
    return ans;
}
void doit()
{   for (int i=0;i<N;i++)
    for (int j=0;j<N;j++)
        e[i][j].c=e[i][j].f=0;
    //add edges
    printf("%d\n",ford(s,t,N));
}

//algo2:  
//Dinic
//O(n*n*m)
struct dinic{
    int g[N],d[N],G[N];
    int np[M],to[M],cap[M];
    int n,s,t,e;
    void AddEdge(int x,int y,int z,int z2=0)
         {  to[e]=y;cap[e]=z;
            np[e]=g[x];g[x]=e++;

            to[e]=x;cap[e]=z2;
            np[e]=g[y];g[y]=e++;
         }
    void init(int n)
         {  this->n=n;e=0;
            memset(g,255,sizeof(g));
         }
    int maxflow(int s,int t)
         {  this->s=s; this->t=t;
            int ans=0;
            while (bfs())
                {for(int i=0;i<n;i++)G[i]=g[i];
                 ans+=dfs(s,inf);
                }
            return ans;
         }
    bool bfs()//建立层次图
    {
        memset(d,-1,sizeof(d));
        queue<int > q;
        q.push(s);
        d[s]=0;
        while(!q.empty())
        {
            int x=q.front(); q.pop();
            for(int i=g[x];~i;i=np[i])
                 {
                     int y=to[i];
                     if(cap[i]&&d[y]==-1)
                     {  d[y]=d[x]+1;
                        if(y==t)return 1;
                        q.push(y);
                     }
                 }
        }
        return 0;
    }

   int dfs(int x,int sum)
    {
        if (x==t|| sum==0) return sum;
        int ss=sum,ret;
        for(int &i=G[x];~i;i=np[i]) //当前弧优化
        {
            int y=to[i];
            if(d[y]==d[x]+1&&(ret=dfs(y,min(sum,cap[i]))))
                {
                    cap[i]-=ret;
                    cap[i^1]+=ret;
                    sum-=ret;
                    if (!sum) break;
                }
        }
        return ss-sum;
    }
}it;

SAP的两个优化:

GAP优化——如果一次重标号时,出现距离断层,则可以证明ST无可行流,此时则可以直接退出算法。
当前弧优化——为了使每次找增广路的时间变成均摊O(V),还有一个重要的优化是对于每个点保存“当前弧”:初始时当前弧是邻接表的第一条弧;在邻接表中查找时从当前弧开始查找,找到了一条允许弧,就把这条弧设为当前弧;改变距离标号时,把当前弧重新设为邻接表的第一条弧。

//algo3_1:
//SAP
//O(n*n*m)
struct SAP{
    int g[N],d[N],G[N],pre[N],numd[N];
    int np[M],to[M],cap[M];
    int n,e,s,t;
    void AddEdge(int x,int y,int z,int z2=0)
        {  to[e]=y;cap[e]=z;
           np[e]=g[x];g[x]=e++;

           to[e]=x;cap[e]=z2;
           np[e]=g[y];g[y]=e++;
        }
    void init(int n)
        {   this->n=n;
            e=0;memset(g,255,sizeof(g));
        }
    void bfs()//先用广度优先算出每个点的d值
        {   memset(numd,0,sizeof(numd));
            for(int i=0; i<n; i++)
                numd[d[i]=n]++;
            d[t]=0; numd[n]--; numd[0]++;
            queue<int> Q;Q.push(t);
            while(!Q.empty())
            {   int v=Q.front();Q.pop();
                int i=g[v];
                while(i!=-1)
                    {
                        int u=to[i];
                        if(d[u]<n){i=np[i];continue;}
                        d[u]=d[v]+1;
                        numd[n]--;numd[d[u]]++;
                        Q.push(u);
                        i=np[i];
                    }
            }
        }

    int maxflow(int s,int t)
    {   this->s=s; this->t=t;
        for(int i=0;i<n;i++)
            G[i]=g[i];
        int max_flow=0;
        bfs();
        int u=s ;//从源点搜一条到汇点的增广路
        while(d[s]<n)//就算所有的点连成一条线源点的d值也是最多是n-1
        {
            if(u==t)//如果找到一条增广路径
            {
                    int cur_flow=inf,neck;//找到那条瓶颈边
                    for(int from=s; from!=t; from=to[G[from]])
                        if(cur_flow>cap[G[from]])
                        {   neck=from;cur_flow=cap[G[from]];}


                for(int from=s; from!=t; from=to[G[from]]) //修改增广路上的边的容量
                    {
                    int tmp=G[from];
                    cap[tmp]-=cur_flow;
                    cap[tmp^1]+=cur_flow;
                    }
                max_flow+=cur_flow;//累加计算最大流
                u=neck;//下一次搜索直接从瓶颈边的前一个节点搜起
            }

            int i;
            for(i=G[u];~i; i=np[i]) //从当前点开始找一条允许弧
            if(cap[i]&&d[u]==d[to[i]]+1)//如果找到跳出循环
                break;

            if(i!=-1)//找到一条允许弧
            {
                G[u]=i;//从点u出发的允许弧的地址
                pre[to[i]]=u;//允许弧上下一个点的前驱为u
                u=to[i];//u变成下一个点继续搜直到搜出一条增广路
            }
            else  //如果没有搜到允许弧
            {
                numd[d[u]]--; //d[u]将被修改所以numd[d[u]]减一
                if(!numd[d[u]]) break;  //如果没有点的d值为d[u]则不可能再搜到增广路结束搜索
                G[u]=g[u];  //当前点的允许弧为第一条边
                int tmp=n;
                for(int j=g[u]; ~j; j=np[j]) //搜与u相连的点中d值最小的
                    if(cap[j]&&tmp>d[to[j]])tmp=d[to[j]];

                d[u]=tmp+1; //修改d[u]
                numd[d[u]]++;
                if(u!=s)u=pre[u];//从u的前驱搜,因为从u没有搜到允许弧
            }
        }
    return max_flow;
    }
}it;

//algo3_2:
//SAP
//O(n*n*m)
#define N 105
int n,m,np,nc,st,ed;
struct SAP{
    int g[N][N];//直接在原图上操作了
    int d[N],gap[N];
    int n,s,t;
    void AddEdge(int x,int y,int z)
        {
            g[x][y]=z;
        }
    void init(int n)
        {   this->n=n;
            memset(g,0,sizeof(g));
            memset(gap,0,sizeof(gap));
            memset(d,0,sizeof(d));
        }
    int maxflow(int s,int t)
        {  this->s=s;this->t=t;
           int ans=0;
           for(gap[0]=n;d[s]<n;)
                ans+=sap(s,inf);
           return ans;
        }
    int sap(int u,int flow)
        {
            if(u==t) return flow;
            int res=flow;
            for(int i=0;i<n;++i)
                if(g[u][i] && d[u]==d[i]+1)
                {
                    int tmp=sap(i,min(res,g[u][i]));
                    g[u][i]-=tmp,g[i][u]+=tmp;
                    if(!(res-=tmp))return flow;
                }
            if(!--gap[d[u]])d[s]=n;
            ++gap[++d[u]];
            return flow-res;
        }
}it;

//algo4:  
//ISAP  
//O(n*n*m)  
//ISAP 的一个所谓 GAP 优化:  
//由于从 s 到 t 的一条最短路径的顶点距离标号单调递减,且相邻顶点标号差严格等于1,  
//因此可以预见如果在当前网络中距离标号为 k (0 <= k < n) 的顶点数为 0,  
//那么可以知道一定不存在一条从 s 到 t 的增广路径,此时可直接跳出主循环。  
struct ISAP  
{   struct Edge  
    {  
        int from,to,cap,flow;  
        Edge(){}  
        Edge(int a,int b,int c,int d):from(a),to(b),cap(c),flow(d){}  
    };  
    int n,m,s,t;//结点数,边数(含反向弧),源点,汇点  
    vector<Edge> edges;//边表,edges[e]&edges[e^1]互为反向弧  
    vector<int> G[N];//邻接表,G[i][j]表示结点i的第j条边在e数组中的序号  
    bool vis[N];//BFS使用  
    int d[N];//从起点到i的距离  
    int cur[N];//当前弧下标  
    int p[N];//可增广路上的上一条弧  
    int num[N];//距离标号计数  
  
    void AddEdge(int from,int to,int cap)//重边不影响  
    {  
        edges.push_back(Edge(from,to,cap,0));  
        edges.push_back(Edge(to,from,0,0));//容量为0,表示反向弧  
        m=edges.size();  
        G[from].push_back(m-2);  
        G[to].push_back(m-1);  
    }  
  
    void init(int n)  
    {  
        this->n=n;  
        for(int i=0;i<n;++i) G[i].clear();  
        edges.clear();  
    }  
  
    void BFS()//反向  
    {   for (int i=0;i<n;i++)
            d[i]=n+10; //!!!
        memset(vis,0,sizeof(vis));
        queue<int> Q;
        Q.push(t);
        d[t]=0;
        vis[t]=1;
        while(!Q.empty())
        {
            int x=Q.front();
            Q.pop();
            for(int i=0; i<G[x].size(); ++i)
            {
                Edge& e=edges[G[x][i]^1];
                if(!vis[e.from]&&e.cap>e.flow)
                {
                    vis[e.from]=1;
                    d[e.from]=d[x]+1;
                    Q.push(e.from);
                }
            }
        }
    }  
  
    int Augment()  
    {  
        int x=t,a=INF;  
        while(x!=s)  
        {  
            Edge& e=edges[p[x]];  
            a=min(a,e.cap-e.flow);  
            x=edges[p[x]].from;  
        }  
        x=t;  
        while(x!=s)  
        {  
            edges[p[x]].flow+=a;  
            edges[p[x]^1].flow-=a;  
            x=edges[p[x]].from;  
        }  
        return a;  
    }  
  
    int maxflow(int s,int t)//结点数  
    {  
        this->s=s,this->t=t;  
        int flow=0;  
        BFS();  
        memset(num,0,sizeof(num));  
        for(int i=0;i<n;++i) if (d[i]!=n+10)++num[d[i]];//!!! 
        int x=s;  
        memset(cur,0,sizeof(cur));  
        while(d[s]<n)  
        {  
            if(x==t)  
            {  
                flow+=Augment();  
                x=s;  
            }  
            int ok=0;  
            for(int i=cur[x];i<G[x].size();++i)  
            {  
                Edge& e=edges[G[x][i]];  
                if(e.cap>e.flow&&d[x]==d[e.to]+1)//Advance  
                {  
                    ok=1;  
                    p[e.to]=G[x][i];  
                    cur[x]=i;  
                    x=e.to;  
                    break;  
                }  
            }  
            if(!ok)//Retreat  
            {  
                int m=n-1;  
                for(int i=0;i<G[x].size();++i)  
                {  
                    Edge& e=edges[G[x][i]];  
                    if(e.cap>e.flow) m=min(m,d[e.to]);  
                }  
                if(--num[d[x]]==0) break;//gap优化  
                num[d[x]=m+1]++;  
                cur[x]=0;  
                if(x!=s) x=edges[p[x]].from;  
            }  
        }  
        return flow;  
    }  
}it;  


algorithm Improved-Shortest-Augmenting-Path
  f <-- 0
  从终点 t 开始进行一遍反向 BFS 求得所有顶点的起始距离标号 d(i)
  i <-- s
  while d(s) < n do
      if i = t then    // 找到增广路
          Augment
          i <-- s      // 从源点 s 开始下次寻找
      if Gf 包含从 i 出发的一条允许弧 (i,j)
          then Advance(i)
         else Retreat(i)    // 没有从 i 出发的允许弧则回退
  return f

procedure Advance(i)
  设 (i,j) 为从 i 出发的一条允许弧
  pi(j) <-- i    // 保存一条反向路径,为回退时准备
  i <-- j        // 前进一步,使 j 成为当前结点

procedure Retreat(i)
  d(i) <-- 1 + min{d(j):(i,j)属于残量网络Gf}    // 称为重标号的操作
  if i != s
     then i <-- pi(i)    // 回退一步

procedure Augment
  pi 中记录为当前找到的增广路 P
  delta <-- min{rij:(i,j)属于P}
  沿路径 P 增广 delta 的流量
  更新残量网络 Gf

~~~~~~~~~~~~~~~~~~~~~~~~~~~增广路与预流推进的分割线~~~~~~~~~~~~~~~~~~~~~~~~~~~~

定义e(u)为节点u上的余流,实际上我们并不需要计算各个h(v),只需将h(s)标为n,其余标为0,算法也能逐步计算出各个点的h。网络流中的节点(除s, t)仅需要满足流入量不小于流出量。其中流入量大于流出量的节点,我们称之为活动节点。预流推进算法就是不断地将活动结点,变为非活动结点,使得预流成为可行流。具体分为两个方法:
①压入操作,选出一个活动顶点u。并依次判断残余网络G中每条边(u, v),若h(u) = h(v) + 1,则顺着这里条边推流,流量变化量df(u,v)=min(e[u],cf(u,v)),同时更新相应的e(u),e(v);
②重标记操作,选出一个活动结点。如果对于残余网络G中每条边(u, v),都有h(v)大于或等于h(u),则需要对u进行重新标号,h(u) = min{h(v) + 1};
具体实现又可以根据对活跃点的推进过程分为:
①一般的预流推进算法(Generic Preflow-Push Algorithm),在残余网络中,维护一个预流,不断对活跃点执行push操作,或者relable操作来重新调整这个预流,直到不能操作。时间复杂度为O(V*E*E);
②先进先出预流推进算法,在残余网络中,以先进先出队列维护活跃点,时间复杂度为O(V*V*V);
③最高标号预流推进算法(Highest Preflow-Push Algorithm),在残余网络图中,每次检查最高标号的活跃点,需要用到优先队列,时间复杂度为O(V*V*E^0.5);


//algo5:
//HLPP
//O(n*n*m^(1/2))
#define N 105
int d[N];
struct HLPP{
    int e[N];
    int g[N][N];
    struct node{
         int index;
         friend bool operator < (const node& a, const node& b)
            {return d[a.index] > d[b.index];}
    };
    int n,s,t;
    void Relabel(int u)
        {
            int m1=inf;
            for(int i=0; i<n; ++i)
                if(g[u][i] && d[i] < m1)m1 =d[i];
            d[u]=m1+1;
        }
    void AddEdge(int x,int y,int z)
        {
            g[x][y]+=z;
        }
    void init(int n)
        {   this->n=n;
            memset(g,0,sizeof(g));
        }
    int maxflow(int s,int t)
        {  node no;
           priority_queue<node> Q;
           memset(e,0,sizeof(e));
           memset(d,0,sizeof(d));
           d[s]=n;
           for(int i=0;i<n;++i)
           if(g[s][i])
                {
                    int df=g[s][i];
                    e[s] -= df;
                    e[i] += df;
                    g[s][i] -= df;
                    g[i][s] += df;
                    no.index = i;
                    if(i!=s&& i!=t) Q.push(no);
                }
           while(!Q.empty())
                {
                 no=Q.top();Q.pop();
                 int u=no.index;
                 while(e[u])
                     {  int i;
                        for(i = 0; i < n; ++i)
                            if(g[u][i] && d[u] > d[i])break;
                        if(i == n) Relabel(u);
                        for(int i=0; i<n; ++i)
                             if(d[u]==d[i] + 1 && g[u][i])
                             {
                                 int df = min(e[u], g[u][i]);
                                 g[u][i] -= df;
                                 g[i][u] += df;
                                 e[u] -= df;
                                 e[i] += df;;
                                 no.index = i;
                                 if(e[i] > 0&&i!=s &&i!=t) Q.push(no);
                             }
                     }
                }
            return e[t];
        }
}it;

~~~~~~~~~~~~~~~~~~~~~~~~~~~最大流与最小费用最大流间的分割线~~~~~~~~~~~~~~~~~~~~~~~~~~~~

struct MCMF
{   struct Edge
    {
        int from,to,cap,flow,cost;
        Edge(){}
        Edge(int a,int b,int c,int d,int e):from(a),to(b),cap(c),flow(d),cost(e){}
    };

    int n,m,s,t;
    vector<Edge> edges;
    vector<int> G[N];
    int inq[N],d[N],p[N],a[N];
    queue<int> Q;

    void init(int n)
    {
        this->n=n;
        for(int i=0;i<n;++i) G[i].clear();
        edges.clear();
    }

    void AddEdge(int from,int to,int cap,int cost)
    {   //printf("%d %d %d %d\n",from,to,cap,cost);
        edges.push_back(Edge(from,to,cap,0,cost));
        edges.push_back(Edge(to,from,0,0,-cost));
        m=edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }

    bool SPFA(int s,int t,int & flow,int & cost)
    {
        memset(d,0x3f,sizeof(d));
        memset(inq,0,sizeof(inq));
        d[s]=0;inq[s]=1;p[s]=0;a[s]=inf;
        Q.push(s);
        while(!Q.empty())
        {
            int u=Q.front();Q.pop();
            inq[u]=0;
            for(int i=0;i<G[u].size();++i)
            {
                Edge& e=edges[G[u][i]];
                if(e.cap>e.flow&&d[e.to]>d[u]+e.cost)
                {
                    d[e.to]=d[u]+e.cost;
                    p[e.to]=G[u][i];
                    a[e.to]=min(a[u],e.cap-e.flow);
                    if(!inq[e.to]) {Q.push(e.to);inq[e.to]=1;}
                }
            }
        }
        if(d[t]==inf) return false;
        flow+=a[t];//固定流量k,检查,若flow+a>=k,只增加k-flow个单位
        cost+=d[t]*a[t];
        int u=t;
        while(u!=s)
        {
            edges[p[u]].flow+=a[t];
            edges[p[u]^1].flow-=a[t];
            u=edges[p[u]].from;
        }
        return true;
    }
    int mincost(int s,int t)
    {
        int flow=0,cost=0;
        while(SPFA(s,t,flow,cost));
        return cost;
    }
}it;


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值