题目描述
难度分:1200
输入n(1≤n≤100)和长为n的数组a(1≤a[i]≤100)。
输入m(1≤m≤100)和长为m的数组b(1≤b[i]≤100)。
你可以从a、b中各选一个数字,组成一个数对(a[i],b[j]),要求|a[i]−b[j]|≤1。
选过的数字不能再选,最多可以选出多少个数对?
输入样例1
4
1 4 6 2
5
5 1 5 7 9
输出样例1
3
输入样例2
4
1 2 3 4
4
10 11 12 13
输出样例2
0
输入样例3
5
1 1 1 1 1
3
1 2 3
输出样例3
2
算法
匈牙利算法
这个题很容易就能看出来是二分图的最大匹配模型,把数组a中的元素用[1,n]编号,b中的元素用[1+n,m+n]编号。如果|a[i]−b[j]|≤1,说明i和j之间有边连接。
-
按照这个逻辑,双重循环建无向图。
-
接下来在这个无向图上跑匈牙利算法做二分图的最大匹配即可。
复杂度分析
时间复杂度
匈牙利算法的时间复杂度为O(ne),其中n为a中的元素数量,相当于图中“左边”顶点a的数量,它们要去匹配“右边”顶点b的数量。而e是图中边的数量,在本题中最差情况下是个稠密图,边的数量是nm。因此,整个算法的时间复杂度为O(n2m)。
空间复杂度
空间消耗的瓶颈在于图的邻接表、标记数组st和匹配数组match。它们的空间是同一级别,均为O(n+m),这也是整个算法的额外空间复杂度。
C++ 代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 110;
int n, m, a[N], b[N], match[N<<1];
vector<int> graph[N<<1];
bool st[N<<1];
bool find(int u) {
for(int v: graph[u]) {
if(!st[v]) {
st[v] = true;
if(match[v] == 0 || find(match[v])) {
match[v] = u;
return true;
}
}
}
return false;
}
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
scanf("%d", &m);
for(int i = 1; i <= m; i++) {
scanf("%d", &b[i]);
}
for(int i = 1; i <= n + m; i++) {
graph[i].clear();
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
if(abs(a[i] - b[j]) <= 1) {
graph[i].push_back(j + n);
graph[j + n].push_back(i);
}
}
}
memset(match, 0, sizeof(match));
int ans = 0;
for(int i = 1; i <= n; i++) {
memset(st, 0, sizeof st);
if(find(i)) ans++;
}
printf("%d\n", ans);
return 0;
}