网络流

来源:互联网 发布:linux 网卡配置文件 编辑:程序博客网 时间:2024/06/17 00:08

最大网络流

思路:

不断寻找增广路

#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……

原创粉丝点击