其他分享
首页 > 其他分享> > 网络流 拆点+最小割点集

网络流 拆点+最小割点集

作者:互联网

P4662 [BalticOI 2008]黑手党

题目描述

Byteland 国警方收到了一条匿名举报,其中说当地黑帮老大正计划一次从港口到郊区仓库的运输。警方知道运输的时间并且知道运输需要用到国家的高速公路网。

高速公路网包含双向的高速公路段,每个路段直接连着两个不同的收费站。一个收费站可能与很多其他的收费站相连。汽车只能通过收费站进入或离开高速公路网。据所知,黑帮会距港口边最近的收费站进入高速公路,从距仓库最近的收费站离开(不会在出高速后重新进入)。特警组位于选定的收费站处。当运输车辆进入被监控的收费站时,它就会被警察抓住。

从这个角度看,最简单的办法就是在每个收费站处都安排特警班。然而,控制一个收费站需要特定的费用,每个收费站费用不同。警方想要让花费最小,所以他们需要制定一个收费站的最小控制集,这个集合满足两个条件:

所有从港口到仓库的交通必须至少经过集合中的一个收费站
监控这些收费站的费用(即监控每一个收费站费用之和)最小
你可以假设使用高速公路可以从港口到仓库。

任务

写一个程序能够:

从标准输入中读取高速公路网,监控代价和运输的起点和终点
找到收费站的最小控制集
输出这个集合到标准输出

输入格式

标准输入的第一行包含两个整数 n 和 m,表示收费站的总数和公路段的总数。收费站按 1 到 n 标号;

第二行包含两个整数 a 和 b,分别表示距港口和仓库最近的两个收费站编号;

接下来 n 行表示控制费用,第 i 行包含一个整数,表示第 i 个收费站的控制费用 c;

接下来 m 行表示高速公路网,第 j 行包含两个整数 x 和 y,表示在 x 和 y 收费站之间有一条公路段相连。每一条高速公路段只出现一次。

输出格式

唯一的一行输出应包含最小控制集中收费站的编号,以递增顺序输出,用一个空格分隔。

如果有多于一个最小控制集,你的程序可以输出任意一个。

输入

5 6
5 3
2
4
8
3
10
1 5
1 2
2 4
4 5
2 3
3 4

输出

1 4

思路

拆点之后。把费用当流量,就是最小割。
我们在残余网络DFS。不经过满流的边,那么如果一个点拆的2个点一个在S集合。一个在T集合。那么这个点就是选择的。因为源汇可以拆
我们应该建立虚的源汇。

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

const int maxn = 2e5 + 10;
const int maxm= 1e6+ 10;
const int INF = 0x3f3f3f3f;

//注释为弧优化
struct max_Folw {
    int d[maxn], cur[maxn], start, tend;
    int Q[maxn * 2];
    struct node {
        int to, cap, flow, next;
    } edge[maxm << 1];

    int head[maxn];
    bool vis[maxn];
    int cnt;

    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] = {to, cap, 0, 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 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;
    }

    //得到割点集合
    queue<int> q;
    void bfs(){
        memset(vis, 0, sizeof(vis));
        vis[start]=1;
        q.push(start);
        while(!q.empty()){
            int u=q.front(); q.pop();
            for(int i = head[u]; i != -1; i = edge[i].next){
                int to=edge[i].to, w=edge[i].cap-edge[i].flow;
                if(w&&!vis[to]){
                    vis[to]=1;
                    q.push(to);
                }
            }
        }
    }

}flow;
//edge[i].cap-edge[i].flow==0 容量-流量=可流的流量 =0说明已经满流
int a[maxn];
set<int> st;
int main() {

    int n, m, A, B; scanf("%d%d%d%d",&n, &m, &A, &B);
    flow.init(0, 2*n+5);
    flow.AddEdge(0, A, INF);
    flow.AddEdge(B+n, 2*n+5, INF);
    for(int i=1; i<=n; i++){
        scanf("%d", &a[i]);
        flow.AddEdge(i, i+n, a[i]);
    }
    for(int i=1; i<=m; i++){
        int x, y; scanf("%d%d", &x, &y);
        flow.AddEdge(x+n, y, INF);
        flow.AddEdge(y+n, x, INF);
    }
    flow.maxflow();
    flow.bfs();
    for(int i=1; i<=n; i++){
        if((flow.vis[i]&&!flow.vis[i+n])||(!flow.vis[i]&&flow.vis[i+n])){
            st.insert(i);
        }
    }
    for(auto x: st){
        printf("%d ", x);
    }
    printf("\n");

    return 0;
}

标签:cap,割点集,拆点,收费站,flow,最小,int,edge,maxn
来源: https://www.cnblogs.com/liweihang/p/13970049.html