poj 1273 最大流

题目链接:http://poj.org/problem?id=1273

 

a.EK算法:(Edmond-Karp): 用BFS不断找增广路径,当找不到增广路径时当前流量即为最大流。

 

b.dinic算法:不断找最短路。

 

题意:现在有m个池塘(从1到m开始编号,1为源点,m为汇点),及n条水渠,给出这n条水渠所连接的池塘和所能流过的水量,求水渠中所能流过的水的最大容量.

 //http://blog.csdn.net/huzhengnan/article/details/7766446一个写的特别好的博客,包括好几种求最大流的写法;

EK(Edmonds_Karp):不断找增广路径,当找不到增广路径时当前流量即为最大流。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
#define INF 0xfffffff

int F[205][205],C[205][205];
int a[205],p[205];
int n,m,sum;

void EK()
{
    queue<int>q;//BFS找增广路
    while(1)
    {
        memset(a,0,sizeof(a));
        q.push(1);//原点入队
        a[1]=INF;
        while(!q.empty())
        {
            int u=q.front();
            q.pop();
            for(int v=1; v<=m; v++)
            {
                if(!a[v] && C[u][v]-F[u][v]>0)
                {
                    p[v]=u;
                    q.push(v);
                    a[v]=min(a[u],C[u][v]-F[u][v]);
                }
            }
        }
        if(a[m]==0)
            break; /找不到增广路,则当前流即为最大流
        sum+=a[m];
        for(int i=m; i!=1; i=p[i])
        {
            F[p[i]][i]+=a[m];
            F[i][p[i]]-=a[m];
        }
    }
    printf("%d\n",sum);
}

int main()
{
    int x,y,z;
    while(scanf("%d%d",&n,&m)==2)
    {
        memset(F,0,sizeof(F));
        memset(C,0,sizeof(C));
        memset(p,-1,sizeof(p));
        sum=0;//记录最大流
        while(n--)
        {
            scanf("%d%d%d",&x,&y,&z);
            C[x][y]+=z;//可能会有相同边
        }
        EK();
    }
    return 0;
}

flow[u][v]为<u,v>流量,cap[u][v]为<u,v>容量 
a[i]表示源点s到节点i路径上的最小残留量、p[i]记录i的前驱。

反向弧的作用:
意义:前的流到a的流量,来退掉一些以往流到a的流量,是这些被退掉的流量能够通过别的路径到达汇点。
//俗话说,就是原来走3,现在走1我不在你那走了,我在你那少走点吗,能在别人那走更多。起到退回的作用,非常好理解!

空间优化的EK:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
#define INF 0xfffffff

int C[205][205];
int a[205],p[205];
int n,m,sum;

void EK()
{
    queue<int>q;
    while(1)
    {
        memset(a,0,sizeof(a));
        a[1]=INF;
        q.push(1);
        while(!q.empty())
        {
            int u=q.front();
            q.pop();
            for(int v=1; v<=m; v++)
            {
                if(!a[v] && C[u][v]>0)
                {
                    p[v]=u;
                    q.push(v);
                    a[v]=min(a[u],C[u][v]);
                }
            }
        }
        if(a[m]==0) break;
        sum+=a[m];
        for(int i=m; i!=1; i=p[i])
        {
            C[p[i]][i]-=a[m];
            C[i][p[i]]+=a[m];
        }
    }
    printf("%d\n",sum);
}

int main()
{
    int x,y,z;
    while(scanf("%d%d",&n,&m)==2)
    {
        sum=0;
        memset(C,0,sizeof(C));
        memset(p,-1,sizeof(p));
        while(n--)
        {
            scanf("%d%d%d",&x,&y,&z);
            C[x][y]+=z;
        }
        EK();
    }
    return 0;
}

//省掉了一个flow数组。

 

dinic:            https://comzyh.com/blog/archives/568/#Dinic-Code

邻接矩阵实现:

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define INF 0xfffffff

int C[205][205];
int dis[205];
int q[2000],h,r;
int n,m,sum;

int BFS()
{
    memset(dis,-1,sizeof(dis));
    dis[1]=0;
    h=0;
    r=1;
    q[1]=1;
    while(h<r)
    {
        int j=q[++h];
        for(int i=1; i<=m; i++)
        {
            if(dis[i]<0 && C[j][i]>0)  //如果没有被访问过 并且两点之间“联通”
            {
                dis[i]=dis[j]+1;//那么到原点的距离就加一
                q[++r]=i;//队列中的第几个元素是谁
            }
        }
    }
    if(dis[m]>0) return 1; //如果可以找到原点到汇点的通路 那么即找到增广路 返回1
    else return 0; //汇点的DIS小于零,表明BFS不到汇点
}

//Find代表一次增广,函数返回本次增广的流量,返回0表示无法增广
int Find(int x,int low) //找到最小的流量
{
    int a=0;
    if(x==m) return low; //如果当前点是汇点,返回当前增加的流量
    for(int i=1; i<=m; i++)
        if(C[x][i]>0 && dis[i]==dis[x]+1 && (a=Find(i,min(low,C[x][i])) ) )
        {
            //联通 是分成图的下一层 能到汇点
            C[x][i]-=a;
            C[i][x]+=a;
            return a;
        }
    return 0;
}

int main()
{
    int x,y,z,ans;
    while(scanf("%d%d",&n,&m)==2)
    {
        memset(C,0,sizeof(C));
        while(n--)
        {
            scanf("%d%d%d",&x,&y,&z);
            C[x][y]+=z;
        }
        sum=0;
        while(BFS())//成功建立分图
            while(ans=Find(1,INF))//成功找到增广路经
                sum+=ans;
        printf("%d\n",sum);
    }
    return 0;
}

 

dinic邻接表实现

#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;

#define MAXN 210
#define INF 0xfffffff

struct Edge
{
    int st, ed;
    int c;
    int next;
} edge[MAXN << 1];

int n, m;
int s, t;
int ans;
int e = 0;
int head[MAXN];
int d[MAXN];

void init()
{
    int i,j;
    int a,b,c;
    s=1;
    t=m;
    e=0;
    ans=0;
    memset(head,-1 sizeof(head));
    for(i=1; i<=n;i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        edge[e].st=a;
        edge[e].ed=b;
        edge[e].c=c;
        edge[e].next=head[a];
        head[a]=e++;
        edge[e].st=b;
        edge[e].ed=a;
        edge[e].next=head[b];
        head[b]=e++;
    }
}

int bfs()
{
    memset(d,-1,sizeof(d));
    queue<int> q;
    d[s]=0;
    q.push(s);
    int i;
    int cur;
    while(!q.empty())
    {
        cur=q.front();
        q.pop();
        for(i=head[cur]; i!=-1; i=edge[i].next)
        {
            if(d[edge[i].ed]==-1 && edge[i].c > 0)
            {
                d[edge[i].ed]=d[cur] + 1;
                q.push(edge[i].ed);
            }
        }
    }
    if(d[t]<0) return 0;
    return 1;
}

int dinic(int x, int flow)
{
    if(x==t) return flow;
        
    int i,a;
    for(i=head[x]; i!=-1; i=edge[i].next)
    {
        if(d[edge[i].ed]==d[x]+1 && edge[i].c>0 && (a=dinic(edge[i].ed, min(flow,edge[i].c))))
        {
            edge[i].c-=a;
            edge[i^1].c+=a;
            return a;
        }
    }
    return 0;
}

void solve()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        init();
        while(bfs())
        {
            int increment;
            increment=dinic(1, INF);
            ans+=increment;
        }
        printf("%d\n",ans);
    }
}

int main()
{
#ifdef LOCAL
#endif
    solve();
    return 0;
}

SAP有个优化就是 当出现断链时,就可以直接退出,还有个优化是当前弧的优化,这两个优化只需要一句话+一个数组就解决了,相当实惠,好的ISAP执行的效率真的非常高,这个写的ISAP用的是链式前向星法表示。  http://blog.csdn.net/shiqi_614/article/details/7982858(相关博客)

SPFA优化:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define MAXN 500
#define MAXE 40000
#define INF 0x7fffffff
long ne,nv,tmp,s,t,index;
struct Edge
{
    long next,pair;
    long v,cap,flow;
} edge[MAXE];
long net[MAXN];
long ISAP()
{
    long numb[MAXN],dist[MAXN],curedge[MAXN],pre[MAXN];
    long cur_flow,max_flow,u,tmp,neck,i;
    memset(dist,0,sizeof(dist));
    memset(numb,0,sizeof(numb));
    memset(pre,-1,sizeof(pre));
    for(i=1; i<=nv ; i++)
        curedge[i]=net[i];
    numb[nv]=nv;
    max_flow=0;
    u=s;
    while(dist[s]<nv)
    {
        if(u==t)
        {
            cur_flow=INF;
            for(i=s; i!=t; i=edge[curedge[i]].v)
            {
                if(cur_flow>edge[curedge[i]].cap)
                {
                    neck=i;
                    cur_flow=edge[curedge[i]].cap;
                }
            }
            for(i=s; i!=t; i=edge[curedge[i]].v)
            {
                tmp=curedge[i];
                edge[tmp].cap-=cur_flow;
                edge[tmp].flow+=cur_flow;
                tmp=edge[tmp].pair;
                edge[tmp].cap+=cur_flow;
                edge[tmp].flow-=cur_flow;
            }
            max_flow+=cur_flow;
            u=neck;
        }
        for(i=curedge[u]; i!=-1; i=edge[i].next)
            if(edge[i].cap>0 && dist[u]==dist[edge[i].v]+1)
                break;
        if(i!=-1)
        {
            curedge[u]=i;
            pre[edge[i].v]=u;
            u=edge[i].v;
        }
        else
        {
            if(0==--numb[dist[u]]) break;
            curedge[u]=net[u];
            for(tmp=nv,i=net[u]; i!=-1; i=edge[i].next)
                if(edge[i].cap>0)
                    tmp=tmp<dist[edge[i].v]?tmp:dist[edge[i].v];
            dist[u]=tmp+1;
            ++numb[dist[u]];
            if(u!=s) u=pre[u];
        }
    }

    return max_flow;
}
int main()
{
    long i,j,np,nc,m,n;
    long a,b,val;
    long g[MAXN][MAXN];
    while(scanf("%d%d",&ne,&nv)!=EOF)
    {
        s=1;
        t=nv;
        memset(g,0,sizeof(g));
        memset(net,-1,sizeof(net));
        for(i=0; i<ne; i++)
        {
            scanf("%ld%ld%ld",&a,&b,&val);
            g[a][b]+=val;
        }
        for(i=1; i<=nv; ++i)
            for(j=i; j<=nv; j++)
                if(g[i][j]||g[j][i])
                {
                    edge[index].next=net[i];
                    edge[index].v=j;
                    edge[index].cap=g[i][j];
                    edge[index].flow=0;
                    edge[index].pair=index+1;
                    net[i]=index++;
                    edge[index].next=net[j];
                    edge[index].v=i;
                    edge[index].cap=g[j][i];
                    edge[index].flow=0;
                    edge[index].pair=index-1;
                    net[j]=index++;
                }
        printf("%ld\n",ISAP());
    }
    return 0;
}

 

posted @ 2016-09-28 19:27  a_clown_cz  阅读(355)  评论(0)    收藏  举报