[TIOJ] 1253
砲打皮皮
題敘
平面的網格裡上有一些點,每次可以把一直排或橫排消掉,
問最少要幾次。
想法
發現對於所有點對$(u, v)$,至少要滿足$x_u + y_v \geq 1$,
也就是說把$x$畫一邊,$y$畫一邊,中間的所有邊都至少要一個點有被選到,
原題就是二分圖最小點覆蓋,也就是二分圖最大匹配
所以Code就是最大匹配的過程,每次看有沒有辦法直接匹配或是藉由重匹配加新的邊,
複雜度$\mathcal{O}(N^2)$。
AC Code
#pragma GCC optimize("O3,unroll-loops")
#include <bits/stdc++.h>
#pragma pack(0)
#define ll long long
#define pii pair<int, int>
#define pll pair<ll, ll>
#define F first
#define S second
#define pb(x) emplace_back(x)
#define MOD 1000000007
#define MOD2 998244353
#define _INFINITY 9223372036854775807
#define fast ios::sync_with_stdio(0), cin.tie(0)
using namespace std;
int n, k, match[1005], ans, t = 0;
vector<int> adj[1005];
bool vis[1005];
bool bimatch(int x)
{
for (int i : adj[x])
{
if (!vis[i])
{
vis[i] = 1;
if (!match[i] || bimatch(match[i]))
{
match[i] = x;
return 1;
}
}
}
return 0;
}
signed main()
{
while (cin >> n >> k)
{
t++;
if (n == 0 && k == 0)
break;
memset(match, 0, sizeof(match));
ans = 0;
for (int i = 1; i <= n; i++)
adj[i].clear();
for (int i = 1; i <= k; i++)
{
int x, y;
cin >> x >> y;
adj[x].pb(y);
}
for (int i = 1; i <= n; i++)
{
memset(vis, 0, sizeof(vis));
if (bimatch(i))
ans++;
}
cout << "Case #" << t << ":" << ans << '\n';
}
}