zoukankan      html  css  js  c++  java
  • 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;
    }
  • 相关阅读:
    不要在init和dealloc函数中使用accessor
    Xcode6.3真机测试无法选择目标机器问题
    Objective-C基础知识
    深入理解dispatch_sync
    AFNetworking 2.0教程
    使用pngcrush压缩png图片
    自定义custom Tab Bar
    CocoaPods 安装相关问题
    iOS 编程之使用Precompile Prefix Header
    Block传值
  • 原文地址:https://www.cnblogs.com/scau20110726/p/2962371.html
Copyright © 2011-2022 走看看