poj 3204 Ikki's Story I - Road Reconstruction

最大流最小割

分析都在代码注释中

/*

题意:对于一个有向图的最大流,在每条流路中,只能修改一条边,使得修改后,整个网络的最大流可以增大,问有多少条这样的边

注意一点,在一条流路中只能修改一条,好像s->1->2->t,假设每条边的容量都是2,那么最大流的流路只有一条,但是这条流路中,

要想增大一条边的容量而使整个网络的最大流增加是不行的,一定要把所有边容量都增大,这与题意不符,所以这个case输出是0,表示1条都没有



首先我们运行一次最大流,得到最终的残余网络,那么我们怎么找到我们要的割边呢?首先割边一定已经是满流的了,假设我们得到一条满流的边u->v

如果源点s可以到达点u,且点v能到达汇点t的话,那么这条满流边就是我们要找的,那么什么是能到达呢?

原来的有向边中,只有非满流的边才能继续走,满流的边我们看做已经是断开的不能再走(即按残余网络来遍历)

所以我们从源点s出发遍历一次整个图,去到的点染色为1。然后从汇点t出发遍历一次整个图,去到的点染色为2

因为要从汇点反过来遍历整个图,所以我们被迫要另外建一个逆邻接表,同样的,我们就算从源点开始遍历,而原始的邻接表有太多无用的信息

(每条有向边还带出一条反边,这些都是没用的信息),所以我们干脆重新建一个顺邻接表,其实就是把原邻接表有用的信息拿出来单独保存

后来拿出来的顺邻接表放在order[]数组中,而逆邻接表放在reorder[]数组中

而我们最后要检查所有满流的边,看两个顶点是不是分别染色为1,2,所以我们开辟一个队列来专门保存这些满流的边,在full队列中

*/



#include <cstdio>

#include <cstring>

#include <vector>

#include <queue>

using namespace std;

#define N 510

#define M 5010

#define INF 0x3f3f3f3f

#define min(a,b) a<b?a:b



int nume;

struct edge

{

    int u,v,cap,flow,next;

}e[10*M];

int first[N],n,m;

int col[N]; //点染色

vector<int>order[N],reorder[N]; //顺邻接表,逆邻接表,保存不满流的边

queue<int>full; //保存满流的边(边的编号)



void add(int u ,int v ,int cap)

{

    e[nume].u=u; e[nume].v=v; e[nume].cap=cap; 

    e[nume].flow=0; 

    e[nume].next=first[u]; first[u]=nume++;

}



void build()

{

    memset(first,-1,sizeof(first));

    nume=0;

    scanf("%d%d",&n,&m);

    for(int i=1; i<=m; i++)

    {

        int u,v,cap;

        scanf("%d%d%d",&u,&v,&cap);

        if(!cap) continue;

        add(u,v,cap);

        add(v,u,0);

    }

}



void EK(int s, int t)

{

    queue<int>q;

    int a[N],p[N],FLOW=0;

    while(1)

    {

        memset(a,0,sizeof(a));

        a[s]=INF;

        memset(p,-1,sizeof(p));

        q.push(s);

        while(!q.empty())

        {

            int u,v,cap,flow;

            u=q.front(); q.pop();

            for(int k=first[u]; k!=-1; k=e[k].next)

            {

                v=e[k].v; cap=e[k].cap; flow=e[k].flow;

                if(!a[v] && cap>flow) //没有计算过并且可以增流

                {

                    a[v]=min(a[u],cap-flow);

                    p[v]=k;

                    q.push(v);

                }

            }

        }



        if(!a[t]) break;

        for(int k=p[t]; k!=-1; k=p[e[k].u])

        {

            e[k].flow += a[t];

            e[k^1].flow -= a[t];

        }

        FLOW += a[t];

    }



}



void dfs1(int u ,int c) //从源点出发进行遍历

{

    col[u]=c;

    int len=order[u].size();

    for(int i=0; i<len; i++)

    {

        int v=order[u][i];

        if(!col[v]) dfs1(v,c);

    }

}



void dfs2(int u ,int c) //从汇点出发逆遍历

{

    col[u]=c;

    int len=reorder[u].size();

    for(int i=0; i<len; i++)

    {

        int v=reorder[u][i];

        if(!col[v]) dfs2(v,c);

    }

}



void search(int s ,int t)

{

    while(!full.empty()) full.pop();

    for(int i=0; i<n; i++) 

    {

        order[i].clear();

        reorder[i].clear();

    }



    for(int k=0; k<nume; k+=2)

        if(e[k].cap==e[k].flow) //满流的边

            full.push(k);

        else                    //非满流的边

        {

            int u=e[k].u , v=e[k].v;

            order[u].push_back(v);  //保存顺邻接表

            reorder[v].push_back(u); //保存逆邻接表

        }



    memset(col,0,sizeof(col));

    dfs1(s,1);  dfs2(t,2);



    int ans=0;

    while(!full.empty()) //检查所有满流的边

    {

        int x,u,v;

        x=full.front();

        full.pop();

        u=e[x].u; v=e[x].v;

        if(col[u]==1 && col[v]==2) ans++;

    }

    printf("%d\n",ans);

}



int main()

{

    build();

    EK(0,n-1);

    search(0,n-1);

    return 0;

}

 

 

你可能感兴趣的:(struct)