Loading

【做题笔记】网络流24题

Part1.飞行员配对方案问题

Problem

有两个集合 \(A\)\(B\)。给定正整数 \(n\)\(m\)\(A = \{x|1\leq x \leq m\}\)\(B = \{y|m+1\leq y \leq n\}\)

现在要将 \(A\)\(B\) 集合的元素一一配对,有若干个配对关系,形如“\(u\), \(v\) 可凑一对”。

求有多少个元素能配成一对,并求出方案。

Solve

裸的二分图,但是网络流来写。明明匈牙利可以过,但这是网络流的题,所以用网络流做

配对关系可以转换成连边关系,如果 \(u\)\(v\) 可以配对,就将 \(u \to v\),容量为 \(1\)

创建两个点,源点 \(s\),汇点 \(t\)。然后将 \(s\) 连向集合 \(A\) 的所有点,集合 \(B\) 的所有点连向 \(t\),容量为 \(1\)。在用此图跑最大流即可。汇点的最大流即为配对的个数。

由于要求方案,要在 \(EK\) 增广的时候记录所配对的点。因为一个增广路上的除去源点和汇点所有点,都会配对。

记得建反边!!

Code

#include <bits/stdc++.h>
#define int long long
#define H 19260817
#define rint register int
#define For(i,l,r) for(rint i=l;i<=r;++i)
#define FOR(i,r,l) for(rint i=r;i>=l;--i)
#define MOD 1000003
#define mod 1000000007
#define inf LLONG_MAX

using namespace std;

inline int read() {
  rint x=0,f=1;char ch=getchar();
  while(ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
  while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
  return x*f;
}

void print(int x){
  if(x<0){putchar('-');x=-x;}
  if(x>9){print(x/10);putchar(x%10+'0');}
  else putchar(x+'0');
  return;
}

const int N = 1e5 + 10;

struct Node {
  int v, w, nx;
} e[N << 1];

int n, m, s = 0, t = 114, tot = 1, h[N], w[N], ansn, last[N], ans[N];

void add(int u, int v, int w) {
  e[++tot].v = v;
  e[tot].w = w;
  e[tot].nx = h[u];
  h[u] = tot;
}

int bfs() {
  memset(last, -1, sizeof last);
  queue<int> q;
  q.push(s);
  w[s] = inf;
  while(!q.empty()) {
    int x = q.front();
    q.pop();
    if(x == t) break;
    for (int i = h[x]; i; i = e[i].nx) {
      int y = e[i].v, w_ = e[i].w;
      if(w_ > 0 && last[y] == -1) {
        w[y] = min(w[x], w_);
        last[y] = i;
        q.push(y);
      }
    }
  } 
  return last[t] != -1;
}

int EK() {
  int res = 0;
  while(bfs()) {
    res += w[t];
    for (int i = t; i != s; i = e[last[i] ^ 1].v) {
      ans[e[last[i] ^ 1].v] = i;
      e[last[i]].w -= w[t];
      e[last[i] ^ 1].w += w[t]; 
    } 
  }
  return res;
}

signed main() {
  m = read(), n = read();
  For(i,1,m) add(s, i, 1), add(i, s, 0);
  For(i,m+1,n) add(i, t, 1), add(t, i, 0);
  while(1) {
    int u = read(), v = read();
    if(u == -1 && v == -1) break;
    add(u, v, 1);
    add(v, u, 0);
  }
  ansn = EK();
  cout << ansn << '\n';
  For(i,1,m) {
    if(!ans[i] || ans[i] == 114) continue;
    cout << ans[i] << ' ' << i << '\n';
  }
  return 0;
}
posted @ 2023-08-12 16:45  Daniel_yzy  阅读(18)  评论(0编辑  收藏  举报