链接:
题意:
给一个
n×m
的
01
矩阵,两个
1
能匹配当且仅当它们在同一行或者同一列,一个矩阵是好的当且仅当它有唯一的完美匹配,求最少把多少个
题解:
考虑构建一个行列的二分图,如果
最后我们就是要把边分配到点上,使得点的度数为
0
或者
这样我们就可以快速check是否合法了,然后我们再考虑还原边。
假设A部有
lef
个点度数为
0
,B部有
max(l−rig,0)+max(r−lef,0)<cnt
,其中
l
和
代码:
#include <bits/stdc++.h>
#define xx first
#define yy second
#define mp make_pair
#define pb push_back
#define mset(x, y) memset(x, y, sizeof x)
#define mcpy(x, y) memcpy(x, y, sizeof x)
using namespace std;
typedef long long LL;
typedef pair <int, int> pii;
const int MAXN = 30;
int n, m, tot, cnt, f[MAXN];
vector <int> adj[MAXN];
bool vis[MAXN], flg;
inline int Find(int x)
{
while (x ^ f[x])
x = f[x] = f[f[x]];
return x;
}
inline int Dfs(int x, int p, int S)
{
vis[x] = 1;
int cur = S >> x & 1;
for (auto y : adj[x])
if (y ^ p)
cur += Dfs(y, x, S);
if (cur > 2)
flg = false;
return (cur & 1) ^ 1;
}
inline bool Chk(int S)
{
flg = true;
for (int i = 0; i < tot; i ++)
vis[i] = false;
for (int i = 0; i < tot; i ++)
if (!vis[i])
if (!Dfs(i, -1, S) || !flg)
return false;
return true;
}
class Permatch
{
public:
int complete(vector <string> board)
{
n = board.size(), m = board[0].length(), cnt = tot = n + m;
for (int i = 0; i < tot; i ++)
f[i] = i, adj[i].clear();
for (int i = 0; i < n; i ++)
for (int j = 0; j < m; j ++)
if (board[i][j] == '#')
{
if (Find(i) == Find(j + n))
return -1;
f[Find(i)] = Find(j + n);
adj[i].pb(j + n); adj[j + n].pb(i);
cnt --;
}
int sin = 0, lef = 0, rig = 0, ans = tot + 1;
for (int i = 0; i < tot; i ++)
if (adj[i].empty())
sin |= 1 << i, cnt --;
if (!cnt)
return 0;
lef = __builtin_popcount(sin & ((1 << n) - 1)), rig = __builtin_popcount(sin >> n);
for (int i = 0; i < 1 << tot; i ++)
if (!(i & sin) && Chk(i))
{
int l = __builtin_popcount(i & ((1 << n) - 1)), r = __builtin_popcount(i >> n);
if (max(l - rig, 0) + max(r - lef, 0) < cnt)
ans = min(ans, __builtin_popcount(i));
}
if (ans == tot + 1)
return -1;
return ans;
}
};