zoukankan      html  css  js  c++  java
  • POJ 3281 Dining 【最大流】【神建模】

    这个思路是真的想不到。

    一开始想的是源点到每个奶牛的容量为1,然后奶牛到drink和food可以建容量为1的边,但怎么连到汇点就不知道了。

    神来之笔是,把源点跟所有food连边,然后food跟输入指定的奶牛连边,奶牛再按输入跟drink连边,所有drink再去跟汇点连边。【特定奶牛吃了food,再喝drink,再流到汇点 与 任意奶牛吃任意food,然后变成特定奶牛,然后喝指定drink,再到汇点是一回事!】

    但这么做不对,因为我们还要保证每个奶牛为答案的贡献不超过1,所以要把【一个奶牛拆成两个】,所以是源->food->奶牛(左)->奶牛(右)->drink->汇。

    拿vector实现了一次,这样的话网络流的所有实现方法我都写过一遍了

    #include<iostream>
    #include<deque>
    #include<vector>
    #include<cstring>
    #define INF 2e9
    using namespace std;
    
    int N,F,D,T;
    
    struct edge{
        int v,cap,reverse;
    };
    vector<int> edges[1005];//邻接表 
    vector<edge> bian;//所有的边都存在里面 
    
    void addedge(int u,int v,int cap){//u到v一条边,再加一条反向边 
        edge e;
        e.cap=cap;
        e.v=v;
        e.reverse=bian.size()+1;//这条边的反边将建在这条边之后(这条边建完后在bian.size()的位置) 
        bian.push_back(e);
        edges[u].push_back(bian.size()-1);
        
        e.cap=0;
        e.v=u;
        e.reverse=bian.size()-1;
        bian.push_back(e);
        edges[v].push_back(bian.size()-1);
    }
    
    int layer[1005],vis[1005];
    bool count_layer(){
        memset(layer,0,sizeof(layer));
        deque<int> q;
        layer[0]=1; q.push_back(0);
        while( !q.empty() ){
            int u = q.front(); q.pop_front();
            if( u==T ) return true;
            for(int i=0;i<edges[u].size();i++){//所有以u为起点的边的边的索引 
                edge &e = bian[ edges[u][i] ];
                int v=e.v;
                if( !layer[v] && e.cap>0 ){
                    layer[v] = layer[u] + 1;
                    q.push_back( v );
                }
            }
        }
        return false;
    }
    
    int dinic(){
        int max_flow=0;
        while( count_layer() ){
            memset(vis,0,sizeof(vis));
            deque<int> q,path;//记录一路走过来的【点】和【边的索引】 
            q.push_back(0); vis[0]=1;
            while(!q.empty()){
                int u = q.back();
                if( u==T ){
                    int min_u,min_v,min_flow=INF;
                    int p=0;//从0点出发 
                    for(int i=0;i<path.size();i++){
                        edge &e = bian[ path[i] ];
                        if( e.cap<min_flow ){
                            min_u=p;
                            min_flow=e.cap;
                        }
                        p=e.v;
                    }
                    
                    p=0;
                    max_flow+=min_flow;
                    for(int i=0;i<path.size();i++){
                        edge &e = bian[ path[i] ];
                        e.cap-=min_flow;
                        bian[e.reverse].cap+=min_flow;
                    }
                    //回溯
                    while( !q.empty() && q.back()!=min_u ){
                        vis[ q.back() ]=0;
                        q.pop_back();
                        path.pop_back();
                    } 
                }
                else{//T边 
                    int i;
                    for(i=0;i<edges[u].size();i++){
                        edge &e = bian[ edges[u][i] ];
                        if( layer[e.v]==layer[u]+1 && !vis[e.v] && e.cap>0 ){
                            vis[e.v]=1;
                            q.push_back( e.v );
                            path.push_back( edges[u][i] );
                            break;
                        }
                    }
                    if(i==edges[u].size()) {
                        q.pop_back();
                        path.pop_back();
                    }
                }        
                
            }
        }
        return max_flow;
    }
    
    int main(){
        //food编号1 - F
        //把一头牛拆成两头 
        //cows1编号F+1 - F+N 
        //cows2编号F+N+1 - F+N+N 
        //drink编号F+2N+1 - F+2N+D
        cin>>N>>F>>D;
         T=N+N+F+D+1;
        for(int i=1;i<=N;i++){
            int fi,di,food,drink; cin>>fi>>di;
            for(int j=1;j<=fi;j++) cin>>food,addedge(food,F+i,1);//food到奶牛1建一条边
            for(int j=1;j<=di;j++) cin>>drink,addedge(F+N+i,F+2*N+drink,1);//从奶牛2到drink建一条边
        }
    
        for(int i=F+1;i<=F+N;i++) addedge(i,i+N,1);//奶牛1到奶牛2建一条边 
        for(int i=1;i<=F;i++) addedge(0,i,1);//从源点0到所有food建边
        for(int i=F+2*N+1;i<=F+2*N+D;i++) addedge(i,T,1); //从所有drink到汇点F+D+2*N+1建边
         
        cout<<dinic();
        return 0;    
    }
  • 相关阅读:
    《那些年啊,那些事——一个程序员的奋斗史》——48
    《那些年啊,那些事——一个程序员的奋斗史》——49
    《那些年啊,那些事——一个程序员的奋斗史》——47
    《那些年啊,那些事——一个程序员的奋斗史》——46
    《那些年啊,那些事——一个程序员的奋斗史》——46
    如何面对单调重复的任务
    几则关于glibc的locale的笔记
    欢迎大家加入Linux Mobile Research圈子
    Idle函数的三大用途
    几则gdb使用技巧
  • 原文地址:https://www.cnblogs.com/ZhenghangHu/p/9521288.html
Copyright © 2011-2022 走看看