zoukankan      html  css  js  c++  java
  • P4126 [AHOI2009]最小割 网络流

      

    A,B两个国家正在交战,其中A国的物资运输网中有NN个中转站,MM条单向道路。设其中第i (1≤i≤M)i(1iM)条道路连接了v_i,u_ivi,ui两个中转站,那么中转站v_ivi可以通过该道路到达u_iui中转站,如果切断这条道路,需要代价c_ici

    现在B国想找出一个路径切断方案,使中转站ss不能到达中转站tt,并且切断路径的代价之和最小。

    小可可一眼就看出,这是一个求最小割的问题。但爱思考的小可可并不局限于此。现在他对每条单向道路提出两个问题:

    • 问题一:是否存在一个最小代价路径切断方案,其中该道路被切断?
    • 问题二:是否对任何一个最小代价路径切断方案,都有该道路被切断?

    问题1  判定是否为最小割的可行边

    问题2  判定是否为最小割的必须边

    #include<bits/stdc++.h>
    using namespace std;
    inline int read(){//读优 
        int x=0;char ch=getchar();
        while(ch<'0'||ch>'9')ch=getchar();
        while(ch>='0'&&ch<='9'){
            x=x*10+ch-'0';ch=getchar();
        }
        return x;
    }
    int n,m,s,t;
    struct Edge{
        int u,v,w,nxt;
    }e[120010];
    int head[4010],cnt=1;//注意:cnt必须从1开始,因为加边是n和n+1,偶数和偶数+1可以通过异或转化,具体请自行推导 
    inline void add(int u,int v,int w){//前向星加边 
        e[++cnt].u=u;
        e[cnt].v=v;
        e[cnt].w=w;
        e[cnt].nxt=head[u];
        head[u]=cnt;
    }
    int dis[4010];
    int bfs(){
        queue<int>q;//广搜队列 
        q.push(s);
        memset(dis,-1,sizeof(dis));//所有编号赋初值 
        dis[s]=0;//初始原点编号,让其可以朝下分层 
        while(!q.empty()){
            int u=q.front();q.pop();//取出队首 
            for(int i=head[u];i;i=e[i].nxt){
                if(e[i].w>0&&dis[e[i].v]==-1){//如果此点可以流并且它没有被编号过 
                    dis[e[i].v]=dis[u]+1;//分层编号
                    q.push(e[i].v);//入队 
                    if(e[i].v==t)return 1;//如果将汇点编完号,返回,示意dinic继续找增广 
                }
            }
        }
        return 0;//找不到一条可行流,示意dinic返回 
    }
    int dfs(int x,int f){
        if(x==t||f==0)return f;//如果搜到了增广路或者没有流量走不下去 
        int used=0;//定义已经流出的流量 
        for(int i=head[x];i;i=e[i].nxt){
            if(e[i].w>0&&dis[e[i].v]==dis[x]+1){//如果此边能走通并且编号正确(根据dinic,只有是下一编号才能走通) 
                int k=dfs(e[i].v,min(e[i].w,f));//向下流,注意处理流量!!!一定要二者最小的! 
                if(k==0)continue;//无法流通,继续处理下一条边 
                used+=k;f-=k;//减少剩余流量,增加流出流量 
                e[i].w-=k;e[i^1].w+=k;//对正向弧和反向弧做流量处理
                if(f==0)break;//剩余流量为0,结束 
            }
        }
        if(used==0)dis[x]=-1;//无法下流,使其退出分层,下次不用再走,否则浪费效率 
        return used;//返回可行流的最大流量 
    }
    int dinic(){
        int flow=0;//最大流 
        while(bfs())
            flow+=dfs(s,0x7fffffff);//加上每次增广可继续下流的流量 
        return flow;
    }
    int dfn[4010],low[4010],vis[4010],scc[4010],num,cntt;
    stack<int>st;
    void tarjan(int u){//tarjan模板,判scc分量,见模板题,不做详细解释 
        st.push(u);vis[u]=1;
        dfn[u]=low[u]=++cntt;
        for(int i=head[u];i;i=e[i].nxt){
            if(e[i].w==0)continue;//注意,满流时无法继续,是本题的关键点 
            if(dfn[e[i].v]==0){
                tarjan(e[i].v);
                low[u]=min(low[u],low[e[i].v]);
            }
            else if(vis[e[i].v]==1)
                low[u]=min(dfn[e[i].v],low[u]);
        }
        if(dfn[u]==low[u]){
            ++num;
            while(1){
                int top=st.top();st.pop();
                vis[top]=0;scc[top]=num;
                if(top==u)break;
            }
        }
    }
    int main(){
        n=read();m=read();s=read();t=read();
        for(int i=1;i<=m;++i){
            int u,v,w;
            u=read();v=read();w=read();
            add(u,v,w);add(v,u,0);//加边,正向弧和反向弧 
        }
        int flow=dinic();//尽管flow并没有用,但调试较为方便 
        for(int i=1;i<=n;++i)
            if(scc[i]==0)
                tarjan(i);//如果没有判过scc,跑一遍,求出它属于的联通块
        //判断方法和公式见前面 
        for(int i=2;i<cnt;i+=2){//这样才能跳到每一条正向边 
            int u=e[i].u,v=e[i].v;
            if(e[i].w==0&&scc[u]!=scc[v]){//记得判断满流
                printf("1 ");
                if(scc[u]==scc[s]&&scc[v]==scc[t])printf("1");
                else printf("0");
            }
            else printf("0 0");
            printf("
    ");
        }
        return 0;
    } 
    View Code
  • 相关阅读:
    《结对-航空购票系统-测试过程》
    《结对-航空购票系统-开发过程》
    课后作业-阅读任务-阅读提问-2
    《1005-构建之法:现代软件工程-阅读笔记》
    《团队-记事本程序-代码设计规范》
    Python作业:jieba库
    Python第四周作业:青蛙跳台阶、欧式距离、验证码校验、大小写互换、凯撒加密、身份证号处理
    Python汉诺塔问题
    第四周(1):利用Python计算π的值,并显示进度条
    Python第二周(1):凯撒密码B,括号配对测试,字符串反码,计算矩形面积,快乐的数字
  • 原文地址:https://www.cnblogs.com/bxd123/p/11252216.html
Copyright © 2011-2022 走看看