题意
题解
A 国是一个二分图,左右部节点奇偶性不同,则在图的最大团中,左部、右部各至多包含一个节点。
B 国的补图是一个二分图,左右部节点奇偶性不同。原图的最大团等于补图的最大独立集,最大独立集等于图中节点数减去最小点覆盖。二分图中最小点覆盖等于最大二分匹配,那么求最大团的问题最终转化为求二分图的最大匹配。
枚举 A 国在最大团中的点集 S S S,其规模最多为 2 2 2,仅考虑与 S S S 所有点都连边的 B B B 国中的节点,求最大团即可。
总时间复杂度 O ( A 2 B 3 ) O(A^2B^3) O(A2B3)。实际上是一个较宽松的上界,求增广路的访问标记可以通过时间戳进行优化。
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
typedef long long ll;
const int MAXN = 3005;
int T, A, B, M, clk;
int ax[MAXN], bx[MAXN];
bool fr[MAXN][MAXN], can[MAXN];
vector<int> G[MAXN];
int match[MAXN], used[MAXN];
bool dfs(int v)
{
used[v] = clk;
for (int u : G[v])
{
if (!can[u])
continue;
int w = match[u];
if (w == -1 || (used[w] != clk && dfs(w)))
{
match[v] = u, match[u] = v;
return 1;
}
}
return 0;
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> T;
while (T--)
{
cin >> A >> B >> M;
for (int i = 0; i < A; ++i)
cin >> ax[i];
for (int i = 0; i < B; ++i)
cin >> bx[i];
for (int i = 0; i < A; ++i)
for (int j = 0; j < B; ++j)
fr[i][j] = 0;
for (int i = 0; i < M; ++i)
{
int u, v;
cin >> u >> v;
--u, --v;
fr[u][v] = 1;
}
for (int i = 0; i < B; ++i)
G[i].clear();
for (int i = 0; i < B; ++i)
for (int j = i + 1; j < B; ++j)
if ((bx[i] & 1) != (bx[j] & 1) && !(__builtin_popcount(bx[i] | bx[j]) & 1))
G[i].pb(j), G[j].pb(i);
clk = 0;
for (int i = 0; i < B; ++i)
used[i] = -1, can[i] = 1, match[i] = -1;
int m = 0;
for (int i = 0; i < B; ++i)
if (bx[i] & 1)
m += dfs(i);
int res = B - m;
for (int i = 0; i < B; ++i)
can[i] = 0;
++clk;
for (int i = 0; i < A; ++i)
{
vector<int> vs;
for (int j = 0; j < B; ++j)
if (fr[i][j])
vs.pb(j);
for (int v : vs)
can[v] = 1, match[v] = -1;
int m = 0;
for (int v : vs)
if ((bx[v] & 1) && can[v])
m += dfs(v);
res = max(res, (int)vs.size() - m + 1);
for (int v : vs)
can[v] = 0;
++clk;
}
for (int i = 0; i < A; ++i)
for (int j = i + 1; j < A; ++j)
{
if ((ax[i] & 1) == (ax[j] & 1))
continue;
vector<int> vs;
for (int v = 0; v < B; ++v)
if (fr[i][v] && fr[j][v])
vs.pb(v);
for (int v : vs)
can[v] = 1, match[v] = -1;
int m = 0;
for (int v : vs)
if ((bx[v] & 1) && can[v])
m += dfs(v);
res = max(res, (int)vs.size() - m + 2);
for (int v : vs)
can[v] = 0;
++clk;
}
cout << res << '\n';
}
return 0;
}