网络流

最大网络流

思路:

不断寻找增广路

#include<bits/stdc++.h>
#define MAX 10000
#define CanToNode Edges[CanTo[Top][i]]
#define Min(a,b) (a)<(b)?(a):(b)
using namespace std;
struct EdgeType{
    int  To, Cap, Flow;     //To能去的点,Cap容量,Flow流量 
};
struct NetworkType{
    int N,M;
    int Layer[MAX+5];   //存层数 
    vector <EdgeType> Edges;            //存下每条边,会发现,所有偶数下标都是存的正向边,奇数下标都是存的反向边 
    vector <int> CanTo[MAX+5];          //存每个点可以走到的边【真正的是存Edges里的下标,因此是采用的邻接表存储【其实应该就是前向星!不过这两个总感觉很神似orz…… 
    //但是为什么要这样存不直接 vector <EdgeType> CanTo[MAX+5]; ?
    //因为这样好定位反向边,因为直接Edges n+1或n-1即是反向边,而直接领接表存不好确定谁是谁的反向边
    /*举例 连边 1 2 10
    则 Edges[0]={2,10}; CanTo[1][0]=0; Edges[1]={1,10} CanTo[2][0]=1; 
    所以1节点的第一条边便是存在Edges的下表为0当中的,调用的时候直接Edges[CanTo[n][i]] 
    并且找0的反向边是1, 1的反向边是0;
    再连边2 3 10 
    Edges[2]={3,10}; CanTo[2][1]=2; Edges[3]={2,10} CanTo[3][0]=3;
    也即 
     */ 
    void AddEdge(int From, int To, int Cap)     //连点 
    {
        Edges.push_back((EdgeType){To,Cap,0});  //正向边,容量为Cap 
        Edges.push_back((EdgeType){From,0,0});      //反向边,容量为0【解释一波要 
        int T=Edges.size();                             //这里为了存CanTo这个数组用 
        CanTo[From].push_back(T-2);                     //正向边,是T-2 
        CanTo[To].push_back(T-1);                       //反向边,是T-1 
    }

    void Test()     //用来测试而已…… 
    {
        for (int i=1; i<=N; i++)
        {
            printf("The Node is:%d  CanTo Many:%d",i,CanTo[i].size());
            for (int j=0; j<CanTo[i].size(); j++) printf("\n%d : To: %d, Flow: %d, Cap: %d, In Array<Edges>'s Top:%d",j,Edges[CanTo[i][j]].To,Edges[CanTo[i][j]].Flow,Edges[CanTo[i][j]].Cap,CanTo[i][j]);
            printf("\n\n");
        }
    } 

    bool Bfs(int S, int E)      //用来分层
    {
        queue <int> Line;
        memset(Layer,0,sizeof(Layer));
        Layer[S]=1;//注意这里必须要赋成1而不能0,因为后面是用!Layer来判断的,所以赋0,起点的层数会被更改 
        Line.push(S);
        while (!Line.empty())       //一直增广直到不能增break 
        {
            int Top=Line.front();
            Line.pop();
            for (int i=0; i<CanTo[Top].size(); i++) if (!Layer[CanToNode.To] && CanToNode.Flow<CanToNode.Cap)   //如果这个节点没有走过并且流量小于容量则可以增广。【这里就可以解释为什么反向边时是减了,因为反向边定义Cap为0,所以通过减来证明可以回流 
            {
                Layer[CanToNode.To]=Layer[Top]+1;
                Line.push(CanToNode.To);
            }       //这里用CanToNode代替了 Edges[CanTo[Top][i]], 简化代码 
        }
        return Layer[E];//这里如果LayerE找到层数,返回真,否则没找到层数即为0,返回0为假 
    } 

    int Dfs(int Top, int MinF, int &E)
    {
        if (Top == E || MinF == 0) return MinF; //如果已经找到终点,或者所能增的流为0,则不用继续找,返回所能增得流
        int Ans=0,Remain;
        for (int i=0; i<CanTo[Top].size(); i++)
        if (Layer[CanToNode.To] == Layer[Top] + 1&& (Remain = Dfs(CanToNode.To,Min(MinF,CanToNode.Cap-CanToNode.Flow),E)) > 0)
        {
            CanToNode.Flow+=Remain;
            Edges[CanTo[Top][i] ^ 1].Flow-=Remain;
            Ans+=Remain; 
            MinF-=Remain;
            if (!MinF) break; 
        }
        return Ans;
    }

    int Dinic(int S, int E)
    {
        int Ans=0;
        while (Bfs(S,E))
        {
            Ans+=Dfs(S,INT_MAX,E);
        }
        return Ans;
    }

    int EK(int S, int E)        //EK 
    {
        queue <int> Line;
        int FlowM[MAX+5],Pre[MAX+5],PreEdge[MAX+5],Ans=0;   //FlowM存所找到的增广路,Pre存所找到的上一个节点下标,而PreEdge则是存路 
        while (1)       //一直增广直到不能增break 
        {
            memset(FlowM,0,sizeof(FlowM));
            FlowM[S]=INT_MAX;   //先赋一个INF初值 
            Pre[S]=0;           //标记回溯更改的终点,即之后回溯更改Edge时用的 
            Line.push(S);
            while (!Line.empty())   //Dfs
            {
                int Top=Line.front();
                Line.pop();
                for (int i=0; i<CanTo[Top].size(); i++) if (!FlowM[CanToNode.To] && CanToNode.Flow<CanToNode.Cap)   //如果这个节点没有走过并且流量小于容量则可以增广。【这里就可以解释为什么反向边时是减了,因为反向边定义Cap为0,所以通过减来证明可以回流 
                {
                    FlowM[CanToNode.To]+=Min(FlowM[Top],CanToNode.Cap-CanToNode.Flow);  //存储这个节点所能增广的值,要与当前节点所能增广与所找到的节点所能增广的大小比较取小 
                    Pre[CanToNode.To]=Top;      //存节点 
                    PreEdge[CanToNode.To]=CanTo[Top][i];        //存增光路的边 
                    Line.push(CanToNode.To);
                }       //这里用CanToNode代替了 Edges[CanTo[Top][i]], 简化代码 
            }
            if (!FlowM[E]) break;   //如果没有找到到终点的路,增广完成 
            Ans+=FlowM[E];      //记录最大流 
            int Top=E;      //从终点,借用Pre和PreEdge来更改值 
            while (Pre[Top])
            {
                Edges[PreEdge[Top]].Flow+=FlowM[E];     
                //int AntiTop=PreEdge[Top]&1?PreEdge[Top]+1:PreEdge[Top]-1; //更改反边的下标,如果是%2==0则是正走的,==1则是反走的,存的是先正走再反走,
                //Edges[AntiTop].Flow-=FlowM[E];
                Edges[PreEdge[Top] ^ 1].Flow-=FlowM[E];     //这里是个位运算基础操作[虽然我也不知道,异或1是用来取代反边的 ,上面注释掉的则是原来所写的方法 
                /*详细给自己讲一下
                1^1=0;0^1=1; 即1异或1相当于-1,0异或1相当于+1 
                1^0=1;0^0=0; 即任何一个数异或0位本身 
                所以一个数n异或1;
                则1(2)=000000001
                  n(2)=101001010[随便打的]
                所以最后一位的前面的所有都是其本身,而最后一位则是相应的+1-1,
                所以 0^1=1,1^1=0;
                2^1=3;3^1=2;
                4^1=5;5^1=4;
                则完美匹配这种存储结构
                */ 
                Top=Pre[Top];
            }
        }
        return Ans;
    }
}Network;
int main()
{
    int Start,End,SR,ER,FR;
    cin >> Network.N >> Network.M >> Start >> End;
    for (int i=1; i<=Network.M; i++)
    {
        cin >> SR >> ER >> FR;
        Network.AddEdge(SR,ER,FR);
    }
    //Network.Test();
    //cout << Network.EK(Start,End) << endl;
    cout << Network.Dinic(Start,End);
}

maye看着脑壳昏先把代码存……【第一次写工程代码orz……
回家再补qwq……

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值