题意:同学们在操场跑步(操场看做无限长的数轴),每个同学如果开始跑步那么他跑步的方向不会改变直到时间结束。老师为了检查是否有同学偷懒没有跑步,手上有 份监测报告,每份监测报告说明了在 时刻时, 位置至少有一个人在跑步。问操场上至少有多少名学生没有偷懒在跑步。
分析:将每个时刻的(t,pos) 看作二维平面上的点,那么对应的同学看作斜率为45°,135°的两条直线。将所有点旋转45°,那么所有的直线就是垂直于坐标轴,那么该问题转换成了选取最少的直线覆盖所有点。将第 个点产生的垂直于轴的直线和垂直于 轴的直线,看成点,并且连一条边,建二分图,那么问题转换成了二分图的最小点覆盖问题,而二分图的最小点覆盖等于二分图的最大匹配。
由于n的范围比较大,选择比较高效的二分匹配算法Donic. .
#include <bits/stdc++.h> using namespace std; const int maxn = 5e5; namespace flow { struct edge { int to, cap, flow, rev; }; vector<edge> g[maxn + 10]; int s, t, ndcnt, d[maxn + 10], cur[maxn + 10]; queue<int> q; void init(int ss, int tt, int nn) { s = ss; t = tt; for (int i = 1; i <= ndcnt; ++i) g[i].clear(); ndcnt = nn; } void add(int l, int r, int w) { g[l].push_back((edge){r, w, 0, (int)g[r].size()}); g[r].push_back((edge){l, 0, 0, (int)g[l].size() - 1}); } bool bfs() { for (int i = 1; i <= ndcnt; ++i) d[i] = i == s ? 0 : -1; q.push(s); while (!q.empty()) { int p = q.front(); q.pop(); for (int i = 0; i < (int)g[p].size(); ++i) { edge e = g[p][i]; if (e.cap > e.flow && d[e.to] == -1) { d[e.to] = d[p] + 1; q.push(e.to); } } } return d[t] != -1; } int dfs(int p, int a) { if (p == t) return a; int ans = 0, now; for (int &i = cur[p]; i < (int)g[p].size(); ++i) { edge &e = g[p][i]; if (e.cap > e.flow && d[p] + 1 == d[e.to]) { now = dfs(e.to, min(a, e.cap - e.flow)); e.flow += now; g[e.to][e.rev].flow -= now; ans += now; a -= now; if (!a) break; } } return ans; } int solve() { int ans = 0; while (bfs()) { for (int i = 1; i <= ndcnt; ++i) cur[i] = 0; ans += dfs(s, 1e9); } return ans; } } int n, x[maxn + 10], y[maxn + 10]; int l[maxn + 10], r[maxn + 10], sl, sr; int main() { int T; scanf("%d", &T); while ( T-- ) { int n; scanf("%d", &n); for ( int i = 1; i <= n; ++i ) { int a, b; scanf("%d%d", &a, &b); x[i] = a + b; y[i] = a - b; } sl = sr = 0; for ( int i = 1; i <= n; ++i ) { l[++sl] = x[i]; r[++sr] = y[i]; } sort(l + 1, l + sl + 1); sl = unique(l + 1, l + sl + 1) - l - 1; sort(r + 1, r + sr + 1); sr = unique(r + 1, r + sr + 1) - r - 1; flow::init(1, 2, 2 + sl + sr); for (int i = 1; i <= sl; ++i) flow::add(1, i + 2, 1); for (int i = 1; i <= sr; ++i) flow::add(i + sl + 2, 2, 1); for ( int i = 1; i <= n; ++i ) { x[i] = lower_bound(l + 1, l + sl + 1, x[i]) - l; y[i] = lower_bound(r + 1, r + sr + 1, y[i]) - r; flow::add(x[i] + 2, y[i] + sl + 2, 1); } printf("%d\n", flow::solve()); } }