[AHOI2009]MINCUT 最小割 边存在最小割的唯一性

题目

(https://ac.nowcoder.com/acm/problem/19887)

题目描述

A,B两个国家正在交战,其中A国的物资运输网中有N个中转站,M条单向道路。设其中第i (1 ≤ i ≤ M)条道路连接了vi,ui两个中转站,那么中转站vi可以通过该道路到达ui中转站,如果切断这条道路,需要代价ci。现在B国想找出一个路径切断方案,使中转站s不能到达中转站t,并且切断路径的代价之和最小。 小可可一眼就看出,这是一个求最小割的问题。但爱思考的小可可并不局限于此。
现在他对每条单向道路提出两个问题:
问题一:是否存在一个最小代价路径切断方案,其中该道路被切断?
问题二:是否对任何一个最小代价路径切断方案,都有该道路被切断?
现在请你回答这两个问题。

输入描述:

第一行有4个正整数,依次为N,M,s和t。
第2行到第(M+1)行每行3个正整数v,u,c表示v中转站到u中转站之间有单向道路相连,单向道路的起点是v,终点是u,切断它的代价是c(1 ≤ c ≤ 100000)。
注意:两个中转站之间可能有多条道路直接相连。同一行相邻两数之间可能有一个或多个空格。

输出描述:

对每条单向边,按输入顺序,依次输出一行,包含两个非0即1的整数,分别表示对问题一和问题二的回答(其中输出1表示是,输出0表示否)。
同一行相邻两数之间用一个空格隔开,每行开头和末尾没有多余空格。

输入

6 7 1 6
1 2 3
1 3 2
2 4 4
2 5 1
3 5 5
4 6 2
5 6 3

输出

1 0
1 0
0 0
1 0
0 0
1 0
1 0

思路

问题一:是否有一个最小割经过该边
问题二:是否所有的最小割都经过该边

结论题:
先跑最大流求出任意一个最小割

对残量网络缩点,此时存在图上的边, 可能是满流的边,可能是和S不流通和T连的的点的0流边

然后对于一条满流的边(u,v)

①Scc[u]!=Scc[v],存在(u,v)被割的方案

②Scc[u]Scc[S]&&Scc[v]Scc[T],(u,v)必定被割

#include<bits/stdc++.h>
using namespace std;

const int maxn = 4000 + 10;
const int INF = 0x3f3f3f3f;

//注释为弧优化
struct node {
    int form, to, cap, flow, next;
} edge[2000006];
int head[maxn];
int cnt;

struct max_Folw {
    int d[maxn], cur[maxn], start, tend;

    bool vis[maxn];


    void init(int s, int t) {
        memset(head, -1, sizeof(head));
        cnt=0;
        start=s, tend=t;
    }

    void add(int start, int to, int cap) {
        edge[cnt].form = start;
        edge[cnt].to = to;
        edge[cnt].cap = cap;
        edge[cnt].flow = 0;
        edge[cnt].next = head[start];
        head[start] = cnt++;
    }

    void AddEdge(int start, int to, int cap) {
        add(start, to, cap);
        add(to, start, 0);
    }

    bool BFS() {
        memset(d, -1, sizeof(d));
        int Q[maxn * 2];
        int Thead, Ttail;
        Thead = Ttail = 0;
        Q[Ttail++] = tend;
        d[tend] = 0;
        while (Thead<Ttail) {
            int x = Q[Thead];
            if (x == start)
                return true;
            for (int i = head[x]; i != -1; i = edge[i].next) {
                int temp = edge[i].to;
                if (d[temp] == -1 && edge[i^1].cap > edge[i^1].flow) { //没有标记,且可行流大于0
                    d[temp] = d[x] + 1;
                    Q[Ttail++] = temp;
                }
            }
            Thead++;
        }
        return false;//汇点是否成功标号,也就是说是否找到增广路
    }

    int DFS(int x, int cap) {
        if (x == tend)
            return cap;
        int flow = 0, f;
        //for (int i = cur[x]; i != -1; i = edge[cur[x]=i].next) {
        for (int i = head[x]; i != -1; i = edge[i].next) {
            int temp = edge[i].to;
            if (d[temp] == d[x] - 1 && edge[i].cap > edge[i].flow) {
                f = DFS(temp, min(cap - flow, edge[i].cap - edge[i].flow));
                edge[i].flow += f;
                edge[i ^ 1].flow -= f;
                flow += f;
                if (flow == cap)
                    return flow;
            }
        }
        d[x] = -2;//防止重搜
        return flow;
    }

    int maxflow() {
        int flow = 0, f;
        while (BFS()) {
            //memcpy(cur, head, sizeof head);
            flow += DFS(start, INF);
        }
        return flow;
    }
} flow;

int scc[maxn];
struct Tarjn{
    int low[maxn];
    int dfn[maxn];
    int vis[maxn];
    int T, N=0;
    stack<int> s;
    void tarjn(int u){
        low[u]=dfn[u]=++T;
        s.push(u), vis[u]=1;
        for(int i=head[u]; i!=-1; i=edge[i].next){
            int to=edge[i].to;
            if(edge[i].cap-edge[i].flow==0) continue;//满流不能再访问
            if(!dfn[to]){//没有访问过
                tarjn(to);
                low[u]=min(low[u], low[to]);
            }
            else if(vis[to]){//在栈中
                low[u]=min(low[u], dfn[to]);
            }
        }
        if(low[u]==dfn[u]){
            ++N;
            while(1){
                int now=s.top(); s.pop();
                vis[now]=0;
                scc[now]=N;
                if(now==u){
                    break;
                }
            }
        }
    }
}ta;

int main() {

    int n, m, s, t;
    scanf("%d%d%d%d", &n, &m, &s, &t);
    flow.init(s, t);
    for(int i=1; i<=m; i++) {
        int x, y, c;
        scanf("%d%d%d", &x, &y, &c);
        flow.AddEdge(x, y, c);
    }
    flow.maxflow();

    for(int i=1; i<=n; i++){
        if(!ta.dfn[i]){
            ta.tarjn(i);
        }
    }
    for(int i=0; i<cnt; i+=2){
        int x=edge[i].form, y=edge[i].to;
            
        //edge[i].cap-edge[i].flow==0 容量-流量=可流的流量 =0说明已经满流
        if(edge[i].cap-edge[i].flow==0&&scc[x]!=scc[y]){
            printf("1 ");
        }
        else{
            printf("0 ");
        }
        if(scc[x]==scc[s]&&scc[y]==scc[t]){
            printf("1\n");
        }
        else{
            printf("0\n");
        }
    }

    return 0;
}
/*
5 5 1 5
1 2 5
2 3 3
3 5 3
2 4 2
4 5 2
*/

posted @   liweihang  阅读(167)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
Live2D
欢迎阅读『[AHOI2009]MINCUT 最小割 边存在最小割的唯一性』
点击右上角即可分享
微信分享提示