給$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
|
#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'; } }
|