题意
地球和月球中间有 n n 个太空站,有 艘太空船, k k 个人要从地球前往月球。
已知每个太空站可以容纳无限多的人,每艘太空船的承载人数为 ,以及它们的行驶路线(经过的每一个太空站编号)。
任意两个太空站之间的行驶时间均为 1 1 ,太空船周期行驶(终点站再回到初始站)
乘客可以选择在任意整数时刻下车或上车,这个操作不消耗时间。
假设所有太空船0时刻都在初始站,求所有人从地球转移到月球的最短时间。
如果不能到达,输出0
。
数据范围
, m≤20 m ≤ 20 , k≤50 k ≤ 50 。
题库链接
题解
首先判断是否有解。
很显然只要地球和月球之间存在路径,那么就一定有解。
用并查集维护所有点的连通关系,最后判断地球和月球是否属于同一集合即可。
然后是求最小值。
可以发现对于任意时刻 ti t i ,所有太空船的位置是已知的。
那么我们将太空船的承载人数设为边的容量,乘客设为流量。
当存在流量 f≥k f ≥ k 进入月球时,转移结束。
那么该如何建图?
那么按照时间建分层图,对于时刻 t t 的点 ,建立新点 Pi,t P i , t ,根据太空船的行驶路径连接新边,继续跑最大流,直到流量达到 k k 结束。
具体而言,即:
如果存在太空船 从 e e 行驶到 ,即存在边 e(u,v) e ( u , v ) ,那么对于新的第 t t 层,连接 和 Pv,t P v , t ,流量限制为 Hi H i ,即太空船 i i 的承载上限;
由于人可以在任意太空站上一直等,所以对于所有点 ,连接 Pi,t−1 P i , t − 1 和 Pi,t P i , t ,流量限制为 INF I N F ,因为太空站的承载能力是无限的;
设汇点为初始时刻的月球编号 T0 T 0 ,那么连接 Tt+1 T t + 1 和 Tt T t ,流量限制为 INF I N F ,如此任意时刻新增的流量可以回到 T0 T 0 方便统计。
不能使用ISAP跑最大流,选择用Dinic。
因为Dinic是多次分层,而ISAP是一次分层,无法处理新加进来的点。
循环当前时刻 t t 并直接在残余网络上加边构建分层图,累计流量直到超过 输出 t t 并跳出。
注意不能使用capacity scaling来优化Dinic,这样增加新边后似乎是跑不出最大流的…
复杂度分析
Dinic算法求最大流的复杂度上界为 。
分层图新建 t t 层,复杂度。
E E 最大为 ,最坏情况下只存在一条路径经过所有点从地球到达月球,且限制为1,此时 t t 为 。
所以复杂度的上界为 O(n3×m2×k) O ( n 3 × m 2 × k ) ,对于如此小的 n,m,k n , m , k ,跑起来毫无压力。
参考代码
// Copyright 2018, Skqliao #include <bits/stdc++.h> #define rg register #define rep(i, A, r) for (rg int i = (A), _##i##_ = (r); i < _##i##_; ++i) #define rof(i, A, r) for (rg int i = (A) - 1, _##i##_ = (r); i >= _##i##_; --i) #define ALL(x) (x).begin(), (x).end() #define SZ(x) static_cast<int>((x).size()) typedef long long ll; namespace io { #ifndef ONLINE_JUDGE char gc() { return getchar(); } template <class T> inline T gt() { register T x; std::cin >> x; return x; } template <typename T> inline void pt(T x, const char c = '\n') { std::cout << x << c; } void fflush() {} #else const int MAXSIZE = 1 << 22; inline char gc() { static char In[MAXSIZE], *at = In, *en = In; if (at == en) { en = (at = In) + fread(In, 1, MAXSIZE, stdin); } return at == en ? EOF : *at++; } template <class T> inline T gt() { register char c; while (c = gc(), !isdigit(c) && c != '-') {} register bool f = c == '-'; register T x = f ? 0 : c - '0'; for (c = gc(); isdigit(c); c = gc()) { x = x * 10 + c - '0'; } return f ? -x : x; } char Out[MAXSIZE], *cur = Out; template <typename T> inline void pt(T x, char c = '\n') { static int S[20], *top; top = S; if (x < 0) { *cur++ = '-', x = -x; } do { *++top = x % 10, x /= 10; } while (x); while (top != S) { *cur++ = *top-- + '0'; } *cur++ = c; } void fflush() { fwrite(Out, 1, cur - Out, stdout); cur = Out; } #endif } // namespace io namespace uf { const int MAXN = 20; int Fa[MAXN]; void init(int n) { rep(i, 0, n) { Fa[i] = i; } } int find(int x) { return Fa[x] == x ? x : Fa[x] = find(Fa[x]); } void merge(int x, int y) { x = find(x), y = find(y); if (x != y) { Fa[y] = x; } } } // namespace uf namespace dinic { const int MAXN = 1200 + 5; const int MAXM = 12000 + 5; const int MAXK = 50 + 5; const int INF = INT_MAX; struct Edge { int v, f, nxt; } E[MAXM << 1]; int K, n, S, T; int A[MAXN][MAXK]; int H[MAXN], cntE; int Dis[MAXN]; bool bfs() { static std::queue<int> q; memset(Dis, -1, sizeof Dis); Dis[S] = 0; q.push(S); while (!q.empty()) { int x = q.front(); q.pop(); for (int i = H[x]; ~i; i = E[i].nxt) { int &v = E[i].v; if (E[i].f > 0 && !~Dis[v]) { Dis[v] = Dis[x] + 1; q.push(v); } } } return ~Dis[T]; } void addEdge(int u, int v, int f) { E[++cntE] = (Edge) {v, f, H[u]}; H[u] = cntE; E[++cntE] = (Edge) {u, -f, H[v]}; H[v] = cntE; } int dfs(int x, int maxf) { if (x == T || !maxf) { return maxf; } int curf = 0; for (int i = H[x]; ~i; i = E[i].nxt) { int &v = E[i].v; if (Dis[v] == Dis[x] + 1 && E[i].f > 0) { int flow = dfs(v, std::min(E[i].f, maxf - curf)); E[i].f -= flow, E[i ^ 1].f += flow; curf += flow; if (curf == maxf) { return curf; } } } return curf; } int dinic() { static int ans = 0; while (bfs()) { ans += dfs(S, INF); } return ans; } void init() { cntE = -1; memset(H, -1, sizeof H); n = io::gt<int>(), A[0][0] = io::gt<int>(), K = io::gt<int>(); T = n + 1; uf::init(T + 1); rep(i, 1, A[0][0] + 1) { A[0][i] = io::gt<int>(); A[i][0] = io::gt<int>(); rep(j, 1, A[i][0] + 1) { int x = io::gt<int>(); A[i][j] = ~x ? x : T; if (j > 1) { uf::merge(A[i][j - 1], A[i][j]); } } } if (uf::find(S) != uf::find(T)) { puts("0"); exit(0); } } void addGraph(int t) { rep(i, 0, n + 1) { addEdge(i + (t - 1) * (n + 2), i + t * (n + 2), INF); } addEdge(n + 1 + t * (n + 2), n + 1 + (t - 1) * (n + 2), INF); rep(i, 1, A[0][0] + 1) { int tmp = (t - 1) % A[i][0] + 1; addEdge(A[i][tmp] + (t - 1) * (n + 2) , A[i][t % A[i][0] + 1] + t * (n + 2), A[0][i]); } } int maxFlow() { init(); for (int t = 1; ; ++t) { addGraph(t); if (dinic() >= K) { return t; } } } } // namespace dinic int main() { printf("%d\n", dinic::maxFlow()); return 0; }