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;
}