点点滴滴”

导航

网络流模板

/************************************************************************
网络流的定义好多呀 这里就仅仅标注定义名字吧 详细解释就交给以后的自己啦
==容量网络和网络最大流
1.容量 (容量网络,弧的容量,弧的流量)
2.网络流(可行流)—— 可行流的瞒住条件(弧流量限制条件&&平衡条件)
3.伪流
4.最大流
==链和增广路
1.饱和弧(非饱和弧)
2.零流(非零流)
3.链
4.前向弧(后向弧)
5.增广路
==残留容量和残留网络
1.残留容量(残留网络)
==割&&最小割
1.割
2.S-T割
3.割的容量(割的净流量,最小割
 K

 


¥¥最大流最小割定理

整体把关:
网络最大流的求解分两大类算法:1.增广路算法(包括一般增光路算法Ford-Filkerson算法 最短增光路算法 连续最短增广路算法) 2.预流推进算法(

///一般增广路算法 纯模板
#include <iostream> #include <string.h> #include <cmath> #include <stdio.h> using namespace std; #define INF 100000+10 #define Max 1000 struct Node { int c; ///容量 int f; ///流量 } mapp[Max][Max]; int pre[Max]; ///指明从哪个定点得到的 方便用到追踪的方法得到可改进量
int alphp[Max]; ///用来存放每点之间的可改进量 int flag[Max]; ///标记该点是否已经检查过了 int que[Max]; ///广搜 模拟队列 int qe,qs; ///队尾 队头 int v; ///队列顶点 int n,m; void Ford() { while(1) { ///0是起始点 memset(pre,-1,sizeof(pre)); memset(flag,-1,sizeof(flag)); memset(alphp,-1,sizeof(alphp)); flag[0]=0; pre[0]=0; alphp[0]=INF; ///之上是初始化三个数组 均标记为未使用 0点使用 0点前驱为0 0点的改进量==INF(意为0点可以流出无限多) qs=qe=0; ///队列扫描 que[qe]=0; ///the begin of que[] qe++; while(qs<qe&&flag[n-1]==-1) { v=que[qs]; ///take the top of que[] qs++; for(int i=0; i<n; i++) { if(flag[i]==-1) { ///每次在改的过程 不断更新点 边; ///正方向未满 so改进 if(mapp[v][i].c<INF&&mapp[v][i].f<mapp[v][i].c) { flag[i]=0; pre[i]=v; alphp[i]=min(alphp[v],mapp[v][i].c-mapp[v][i].f); que[qe]=i; qe++; } ///反向有流量(反向流) else if(mapp[i][v].c<INF&&mapp[i][v].f>0) { flag[i]=0; pre[i]=-v; alphp[i]=min(alphp[v],mapp[i][v].f); que[qe]=i; qe++; } } } flag[v]=1; } ///当队列为空的时候 汇点还没有被标记 或者汇点的调整量==0 则这次失败了 跳出while循环 if(flag[n-1]==-1||alphp[n-1]==0) break; ///否者就应该到追踪 更新调整了 int k1=n-1; ///最后一个点 int k2=abs(pre[k1]); ///与最后一个点连接的点(前一个点) int a=alphp[n-1]; ///可调整量 while(1) ///该路线一定是可调整路线 { if(mapp[k2][k1].f<INF) ///进行调整 在原有基础上加上调整量(更新) 正向 { mapp[k2][k1].f+=a; } else ///反向 mapp[k1][k2].f-=a; if(k2==0) ///调整到原点了 break; k1=k2; //继续向前追踪 k2=abs(pre[k2]); } } int maxFlow=0; ///==最大流 for(int i=0;i<n;i++) { for(int j=0;j<n;j++) { if(i==0&&mapp[i][j].f<INF) maxFlow+=mapp[i][j].f; if(mapp[i][j].f<INF) printf("%d->%d:%d\n",i,j,mapp[i][j].f); } } printf("maxFlow==%d\n",maxFlow); } int main() { // freopen("in.txt","r",stdin); int u,v,c,f; cin>>n>>m; for(int i=0;i<n;i++) { for(int j=0;j<n;j++) { mapp[i][j].c=mapp[i][j].f=INF; ///初始化 } } for(int i=0;i<m;i++) ///建图 { cin>>u>>v>>c>>f; mapp[u][v].c=c; mapp[u][v].f=f; } Ford(); }///输入输出input:6 100 1 8 2
0 2 4 3
1 3 2 2
1 4 2 2
2 1 4 2
2 3 1 1
2 4 4 0
3 4 6 0
3 5 9 3
4 5 7 2
output:
0->1:4
0->2:4
1->3:2
1->4:2
2->1:0
2->3:1
2->4:3
3->4:0
3->5:3
4->5:5
maxFlow==8

///最短增光路算法最短增光路算法
最短增广路算法
构造残留网络—>构造层次网络——>BFS(删去不可继续增广的边,点)——>不存在增广路为止;
///连续增光路
构造残留网络——>构造层次网络——>DFS(每次DFS之后回退到起点能够到达的最远的点)——>回退到起点结束;
两者之前的部分是一样的 对应模板后续补上~~~

接下来打几道网络流的题吧~~(都是copy 但是我会详细写过程的!!!)
poj1149 poj1273 poj2112 poj1459 poj1087 poj2301 poj3436 poj1637

poj1149  一般增广路算法
仔细理解题意 
重点在构造网络流
构造方法:
1. 我们将每一个猪圈看成除源点和汇点之外的点(所以我们需要添加源点和汇点)
2.源点和每个猪圈的第一个顾客相连接,边的权值是猪圈开始时猪的数目(如果源点和某个边重合了,则权值之和作为作为该边新的权值) eg:第一个猪圈的第一个顾客是1 第二个猪圈也是 所以源点和1号顾客连边的权值==4
3.顾客j紧跟着顾客i 则边<i,j>=INF ,因为卖家可以在开j之前(就是在i顾客期间 随意更换每个猪圈里猪的数目,所以j可以买到任意多的猪)
4.每个顾客和会点之间的连边是顾客希望买到猪的数目(所以汇点的流入量==顾客想买猪的总数)


第一次插图片 有些丑~~~~啦啦啦 构造的原始图就是这样的!!!!
接下来就是真正的代码啦~~
#include <string.h>
#include <stdio.h>
#include <iostream>

using namespace std;
#define Maxm 1000
#define Maxn 10
#define INF 300000000

int s,t; ///源点 汇点
int cust[Maxn+2][Maxn+2];  ///节点之间的容量(包括源点和汇点)customer
int flow[Maxn+2][Maxn+2];  ///节点之间的流量

void init()
{
    int n,m; ///猪圈数   顾客数
    int num;  ///每个顾客拥有的钥匙数
    int house[Maxm];  ///每个猪圈中猪的数目
    int last[Maxm]; ///每个猪圈的前一个顾客的序号   到追踪的翻版
    int k;  ///第k个猪圈的钥匙
    memset(last,0,sizeof(last));
    memset(house,0,sizeof(house));
    cin>>n>>m;
    s==0;
    t=n+1;  ///定义源点和汇点
    for(int i=1; i<=m; i++)
        cin>>house[i];  ///注意角标从1开始   0给源点了
    for(int i=1; i<=n; i++)
    {
        cin>>num;
        for(int j=0; j<num; j++)
        {
            cin>>k;  ///钥匙的序号
            if(last[k]==0)  ///开通猪圈的第一把钥匙
                cust[s][i]+=house[k];  ///如果有重合边  权值相加
            else
                cust[last[k]][i]=INF; ///如果前面有钥匙开该猪圈  则该连接边的权值无穷大
            last[k]=i;  ///更新其前驱~~
        }
        cin>>cust[i][t];  ///该顾客想买的猪的数量
    }

    ///整个图已经构造完成
}

void Ford()
{
    int pre[Maxn+2];  ///连接边的前一个定点  方便倒追踪  源点标号==-1   其他未标记的点标号初始==-2
    int minflow[Maxn+2];  ///每个定点的可改进量
    ///广搜(必然队列模拟呀)
    int que[Maxn+2];
    int qs,qe; ///队头  队尾
    int v;  ///队列定点==当前检查的点
    int p;   ///可改进量  (cij-fij)
    ///构造零流   假设之前猪圈没有猪
    memset(flow,0,sizeof(flow));

    minflow[0]=INF;
    ///进入广搜
    while(1)
    {
        for(int i=0; i<Maxn+2; i++)
            pre[i]==-2;
        pre[0]=-1;  ///源点
        qs=0;
        que[qs]=0;  ///定点qs入队
        qe=1;
        while(qs<qe&&pre[t]==-2)  ///t是汇点
        {
            ///当qs>=qe时  说明队列为空 标号不可以进行下去了
            v=que[qs];
            qs++;
            for(int i=0; i<t+1; i++) ///从源点到汇点都包含在内的
            {
                if(pre[i]==-2&&(p=cust[v][i]-flow[v][i]))
                {
                    pre[i]=v;
                    que[qe]=i;
                    qe++;
                    minflow[i]=(minflow[v]<p)?minflow[v]:p;
                }
            }
        }
        if(pre[t]==-2)
            break;  ///汇点没有标号  证明源点到汇点没有通路了。。。
        int i,j;
        for(i=pre[t],j=t; i!=-1; j=i,i=pre[i])
        {
            flow[i][j]+=minflow[t];
            flow[j][i]-=flow[i][j];
        }
        for(i=0,p=0; i<t; i++)
            p+=flow[i][t];
        cout<<p<<endl;
    }
}

int main()
{
    init();
    Ford();
    return 0;
}





posted on 2014-12-15 17:28  点点滴滴”  阅读(175)  评论(0编辑  收藏  举报