luogu P4126最小割

题目链接:https://www.luogu.com.cn/problem/P4126
题目大意:给出一个图,和源点和汇点,问图中的边中有哪些是最小割的可行边,哪些是最小割的必须边。
思路:
可行边
可行边的意思是,某一种最小割方案会经过的边,(但我理解的是,最小割的必须边,也就是最小割一定会经过这些边,其他的最小割的方案是在这些边上面添加了一些其他边组成的方案)。
一条边是最小割的可行边的充要条件是:
我们可以思考一下最小割的定义,割去权值和最小的边,使得源点和汇点不连通,那么如果边u-v是最小割的可行边,那么这条边的流量是用完了的。也就是跑完网络流算法之后,权值变为了0,然后割去这条边之后,那么源点和汇点是不连通的(只用正向边),那么这条边的两个点也是不连通的,也就是两个点分属于不同的集合。
总结一下就是一条边是最小割的可行边的充要条件是:
1.满流
2.该边的两个点是不连通的
求法
先跑一下最小割(最大流),然后使用tarjin算法求出点所在的集合(其实看到联通的时候,我是想用并查集判断正向边的连通性,不知道是否可行,稍后尝试之后发出来修改)。如果一条边是满流并且两个点分属于不同的集合,那么这条边是可行边。
必须边
含义:一定在最小割中的边。
以上含义是在其他博客上的,但我觉得有误解,这个必须边是增加这条边的流量可以直接增加最小割的值的边,如果在分层图中表示,那么这些边就是权值和最小的那一层的边,因为其他层的边是受到了这一层的边的流量的制约,在增加这些边的流量之后,总流量才会加大。
充要条件
从含义中可以明显的知道,必须边首先是可行边,必须满足可行边的条件,其次,如果增加这条边的流量,可以增加图的最小割的值,那么也就是从原点到起点还有多余的流量,从汇点到其他点也还有多余的流量。因此,还要满足的条件是源点点和起点是联通的,汇点和终点是联通的。
总结一下就是,一条边是最小割的必须边的充要条件是:
1.是最小割的可行边(判断的时候只需判断满流即可,第二个)
2.源点和起点是联通的,汇点和终点是联通的。
求法:
先求出最小割,再用tarjin判断一下连通性就可以了。

#pragma GCC optimize(2)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <stack>
#include <bitset>
#include <queue>
#include <cstdio>
//#include 
#include <time.h>
using namespace std;
//std::mt19937 rnd(233);
#define pp pair<int,int>
#define ull unsigned long long
#define ls root<<1
#define rs root<<1|1
//#define int long long
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int NINF = 0xc0c0c0c0;
const int maxn = 3e6+7;
const int Maxn = 1e7+7;
const int mod=1e9+7;
struct edge{
    int u,v,w,next;
}e[maxn];
int head[10000],cur[10000],cnt;
int dis[10000];
int s,t;
void add(int a,int b,int c){
    e[cnt]=edge{a,b,c,head[a]};
    head[a]=cnt++;
}

bool bfs(){
    queue<int> q;
    memset(dis,0,sizeof dis);
    dis[s]=1;
    //cout<
    q.push(s);
    while(!q.empty()){
        int u=q.front();
        q.pop();

        for(int i=head[u];i!=-1;i=e[i].next){
            int v=e[i].v,w=e[i].w;
            if(!dis[v] && w>0){
                dis[v]=dis[u]+1;
                q.push(v);
                if(v==t)return 1;
            }
        }
    }
    return 0;
}

int dfs(int u,int flow){
    if(u==t)return flow;
    int tflow=0;
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].v,w=e[i].w;
        cur[u]=e[i].next;
        //cout<
        if(dis[v]!=dis[u]+1)continue;
        if(w<=0)continue;

        int tempf=dfs(v,min(flow,w));
        //cout<<"v="<[i].w-=tempf;
        e[i^1].w+=tempf;
        tflow+=tempf;
        flow-=tempf;
        if(!flow){
            break;
        }
    }
    if(!tflow)dis[u]=0;
    return tflow;
}

int dfn[4010],low[4010],vis[4010],col[4010],tot,num;
int sta[4010],top;
void tarjin(int u){
    sta[++top]=u;
    dfn[u]=low[u]=++tot;
    vis[u]=1;
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].v,w=e[i].w;

        if(w==0)continue;
        if(!dfn[v]){
            tarjin(v);
            low[u]=min(low[u],low[v]);
        }
        else if(vis[v]){
            low[u]=min(dfn[v],low[u]);
        }
    }
    if(dfn[u]==low[u]){
        ++num;
        while(sta[top]!=u){
            int v=sta[top];
            top--;
            vis[v]=0;
            col[v]=num;
        }
        vis[u]=0;
        col[u]=num;
        top--;
    }
}

signed main(){
    int n,m;
    scanf("%d%d%d%d",&n,&m,&s,&t);
    memset(head,-1,sizeof head);
    for(int i=1;i<=m;i++){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
        add(b,a,0);
    }
    int ans=0;
    while(bfs()){
        ans+=dfs(s,inf);
    }
    //cout<
    for(int i=1;i<=n;i++)
        if(!col[i])tarjin(i);
    for(int i=0;i<cnt;i+=2){
        int v=e[i].v,u=e[i].u,w=e[i].w;
        if(w==0 && col[u]!=col[v]){//可行边
            printf("1 ");
            if(col[u]==col[s] && col[v]==col[t])printf("1");
            else printf("0");
        }
        else printf("0 0");
        puts("");
    }
}

你可能感兴趣的:(图论,Tarjin,网络流)