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;
}

 

 

posted @ 2013-03-15 23:53  Titanium  阅读(377)  评论(0编辑  收藏  举报