TIOJ-1069

給$n$個事件,每個事件發生在$t$時間點,座標$(x, y)$,

對於每個事件,可以多派遣一個人去解決,或者讓已經去解決某個事件的人走過來(如果從事件$i$走到事件$j$,必須滿足$t_i>t_j$且$|t_i-t_j|\ge |x_i-x_j|+|y_i-y_j|$)。

問最少需要派遣幾個人才能完成所有事件。

$1\le n\le 1000, 0\le x,y\le 1000, 0\le t< 1440$


這題好棒!!
完全沒想到可以這樣做。

首先考慮建有向圖,如果可以從事件$i$走到事件$j$,就建一條$i\to j$的邊,這樣問題就變成在有向圖上找最少路徑覆蓋整張圖。

然而這樣還不可做QQ,我們再把有向圖的入度出度拆成兩個點,每條$i\to j$的邊改成$i_{out}\to j_{in}$,可以發現這張圖會是二分圖(入度出度不會連邊)。

而在圖上每多一條匹配,象徵意義就是完成事件$i$的人接著去做事件$j$,換句話說,就是可以少派遣一個人!

所以答案會是n-匹配數,其中二分圖最大匹配我們使用匈牙利演算法。
複雜度$\mathcal{O}(VE)$。(理論上$E$是可以到$V^2$的,而$V=N\le 1000$,但是還是能過,匈牙利真神奇)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/*
-------------- | /
| | /
| | /
| * |/ | | ------ *
| | | | / \
| | |\ | | | |\ |
\ | | | \ | | | | \ |
\ | | | \ | | \ / \ |
V | | \ \__/| ----- \ |
*/
#include <bits/stdc++.h>
using namespace std;

#define EmiliaMyWife ios::sync_with_stdio(0); cin.tie(NULL);
/*--------------------------------------------------------------------------------------*/

signed main() { EmiliaMyWife
int t;
cin >> t;
while(t--) {
int n, ans;
cin >> n;
ans = n;
vector<int> t(n), x(n), y(n);
for(int i = 0; i < n; i++)
cin >> t[i] >> x[i] >> y[i];
vector<int> ais(n, -1), bis(n, -1);
vector<vector<int>> edge(n);
vector<bool> vis(n);
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
if(t[i] < t[j] && abs(x[i] - x[j]) + abs(y[i] - y[j]) <= t[j] - t[i]) {
edge[i].push_back(j);
if(!~ais[i] && !~bis[j])
ais[bis[j] = i] = j, ans--;
}
function<bool(int)> dfs = [&] (int u) {
vis[u] = true;
for(int v : edge[u])
if(!~bis[v] || (!vis[bis[v]] && dfs(bis[v])))
return ais[bis[v] = u] = v, true;
return false;
};
for(int i = 0; i < n; i++)
if(!~ais[i] && (fill(vis.begin(), vis.end(), false), dfs(i)))
ans--;
cout << ans << '\n';
}
}