P2071 座位安排——二分图最大匹配

P2071 座位安排

题目背景

公元二零一四年四月十七日,小明参加了省赛,在一路上,他遇到了许多问题,请你帮他解决。

题目描述

已知车上有(N)排座位,有(N*2)个人参加省赛,每排座位只能坐两人,且每个人都有自己想坐的排数,问最多使多少人坐到自己想坐的位置。

输入格式

第一行,一个正整数(N)

第二行至第(N*2+1)行,每行两个正整数(Si_1)(Si_2),为每个人想坐的排数。

输出格式

一个非负整数,为最多使得多少人满意。

输入输出样例

输入 #1复制

4
1 2
1 3
1 2
1 3
1 3
2 4
1 3
2 3

输出 #1复制

7

说明/提示

对于10%的数据 N≤10

对于30%的数据 N≤50

对于60%的数据 N≤200

对于100%的数据 N≤2000

算法提示:二分图的最大匹配

思路

二分图的最大匹配

把人看成是男生,座椅看成是(2*n)个女生

但是这道题一排有两个点,怎么办

我们可以自然地想到,并查集的扩展域

相同的,我们迁移到这道题,一排不是有两个座位吗,我们不是不知道那个连那个吗,直接加个(n)把他们分开不就好了

add(i,x);
add(i,s);
add(i,x+n);
add(i,s+n);

这样我们就可以把男生看成左部点,女生看成右部点,分成完全独立的两部分了

小优化,从minxu那里学到的,可以不用每次都memset的方法,就是你记一个时间戳

每次小于时间的才更新的,和memset0一个效果,具体为什么的见我的这一篇博客

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 200000;
int ne[N], head[N], ver[N], idx;
int match[N], st[N];
int tim;
int n, m;
int res;
void add(int u, int v) {
  ne[idx] = head[u];
  ver[idx] = v;
  head[u] = idx;
  idx++;
}

int find(int x) {
  for (int i = head[x]; i != -1; i = ne[i]) {
    int j = ver[i];
    if (st[j] < tim) {
      st[j] = tim;
      if (match[j] == 0 || find(match[j]) == 1) {
        match[j] = x;
        return 1;
      }
    }
  }
  return 0;
}

int main() {
  memset(head, -1, sizeof(head));
  scanf("%d", &n);
  for (register int i = 1; i <= n * 2; i++) {
    int s, x;
    scanf("%d%d", &s, &x);
    add(i, s);
    add(i, x);
    add(i, s + n);
    add(i, x + n);
  }
  for (register int i = 1; i <= n * 2; i++) {
    ++tim;
    if (find(i)) res++;
  }
  cout << res << endl;
  return 0;
}
原文地址:https://www.cnblogs.com/bangdexuanyuan/p/14075168.html