ZOJ 2532 Internship (网络流求最小割)

原题地址:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1532

思路:说下方法:
1,建图跑一遍最大流
2.从源点和汇点跑一遍正向边的dfs
3.枚举每一条边,如果当前边的两个端点一个能被源点访问到,一个能被汇点访问到,那么这条边就是最小割

具体见代码

#include <cmath>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
#include <stack>
#include <set>
#include <cctype>
#define eps 1e-8
#define INF 0x3f3f3f3f
#define MOD 1e9+7
#define PI acos(-1)
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define CLR(x,y) memset((x),y,sizeof(x))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 1e5 + 5;
int n, m, k, s, t, l;
struct edge {
    int u, v, w, nxt; //v表下一个节点,w表示可行流量,nxt寻找下一条边
} e[200005];
int head[maxn], cnt;
void init_head() {
    cnt = 0;
    CLR(head, -1);
}
void  add_edge(int u, int v, int w) {
    e[cnt].v = v;
    e[cnt].w = w;
    e[cnt].u = u;
    e[cnt].nxt = head[u];
    head[u] = cnt++;

    e[cnt].v = u;
    e[cnt].w = 0;
    e[cnt].u = v;
    e[cnt].nxt = head[v];
    head[v] = cnt++;
}
void init() {
    cnt = 0;
    CLR(head, -1);
}

int dis[maxn];//用于标记层次
bool BFS() {
    CLR(dis, -1);
    dis[s] = 0;
    queue<int>q;
    q.push(s);
    while(!q.empty()) {
        int u = q.front();
        q.pop();
        for(int i = head[u]; ~i; i = e[i].nxt) {
            int v = e[i].v;
            if(dis[v] == -1 && e[i].w > 0) {
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
    }
    return dis[t] != -1;
}
int DFS(int s,  int f) {
    if(s == t) return f;
    int ans = 0;
    for(int i = head[s]; ~i; i = e[i].nxt) {
        int v = e[i].v;
        int w = e[i].w;
        if(w > 0 && dis[v] == dis[s] + 1) {
            int flow = DFS(v,  min(f, e[i].w));
            f -= flow;
            e[i].w -= flow;
            e[i ^ 1].w += flow;
            ans += flow;
        }
    }
    return ans;
}
int Dinic() {
    int ans = 0, tmp;
    while(BFS()) {
        while(tmp = DFS(s,  INF)) {
            ans += tmp;
        }
    }
    return ans;
}
int vis1[maxn], vis2[maxn];
void dfs(int u, int*vist, int op) {//正向边总是是偶数,反向边总是奇数
    vist[u] = true;//是用正向边判断的,正向边有流量就能走
    for(int i = head[u]; i != -1; i = e[i].nxt) {//op==0 op==1
        if(!vist[e[i].v] && e[i ^ op].w != 0) {
            dfs(e[i].v, vist, op);
        }
    }
}


int ans[maxn], num;
int main() {
    while(~scanf("%d %d %d", &n, &m, &l)) {
        if(n + m + l == 0) {
            break;
        }
        s = n + m + 1; //源点
        t = 0; //汇点
        memset(head, -1, sizeof(head));
        int a, b, c;
        cnt = 0;
        num = 0;
        for(int i = 0; i < l; i++) {
            scanf("%d %d %d", &a, &b, &c);
            add_edge(a, b, c); //建立边,正向为c,负向为0
        }
        for(int i = 1; i <= n; i++) {
            add_edge(s, i, INF);
        }
        Dinic();
        memset(vis1, false, sizeof(vis1));
        memset(vis2, false, sizeof(vis2));
        dfs(s, vis1, 0); //从源点向汇点搜索,标记还有剩余流的点
        dfs(t, vis2, 1); //从汇点到源点搜索,标记还有剩余流的点
        int num = 0;
        for(int i = 0; i < l; i++) {//l是边数
        if(e[i << 1].w == 0 && vis1[e[i << 1].u] && vis2[e[i << 1].v]) {
                ans[num++] = i + 1; 
            }//当前边的流量为0并且端点都能被访问到
        }
        if(num) {
            for(int i = 0; i < num; i++) {
                if(i) {
                    printf(" ");
                }
                printf("%d", ans[i]);
            }
        }
        printf("\n");

    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/yiqzq/article/details/81390692
ZOJ