再一次感觉网络流太神奇了qwq
题目链接:[星际转移问题](P4009 汽车加油行驶问题 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn))
受到之前那道[汽车加油行驶问题](P4009 汽车加油行驶问题 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn))按汽油剩余量建图的启发,这题自然而然的就想到了按时间建图,但是怎么按时间建图又成了一个难点.
首先判断是否有解,地球与月亮联通时必定有解. 这一步可以用并查集来判断
DSU dsu(n + 5);
for (int i = 1; i <= m; ++ i) {
for (auto &x : s[i]) cin >> x, ++ x;
for (int j = 0; j < k - 1; ++ j) dsu.merge(s[i][j], s[i][j + 1]);
}
if (dsu.find(0) != dsu.find(1)) {
cout << 0 << '\n';
return 0;
}
由于数据范围很小,我们可以枚举时间,然后跑最大流看是否等于总人数 K K K
本题的所有角色编号
-
源点: S S S
-
汇点: T T T
-
地球: 1 1 1
-
月球: 0 0 0
-
空间站: 0 ∽ n + 1 0 \backsim n+1 0∽n+1 ( ( (默认地球和月球也是空间站 ) ) )
-
太空船: n + 2 ∽ n + 1 + m n+2 \backsim n+1+m n+2∽n+1+m
所以除开源汇点外,节点个数 c n t cnt cnt为 n + m + 2 n+m+2 n+m+2,对于时间 T T T对应的节点编号为 i n d e x + T × ( n + m + 2 ) index+T \times (n+m+2) index+T×(n+m+2)
现在是最重要的建图环节 ! ! ! !!! !!! 设现在枚举到时间 T T T
-
源点向 0 0 0时刻的地球连边, T T T时刻的月球连边
d.AddEdge(d.S, 1, k); d.AddEdge(0 + cnt * T, d.T, k);
-
任意时刻,人都有三个选择
-
太空船 → \rightarrow →空间站
// j时刻人从空间站到飞船 d.AddEdge(s[i][now] + j * cnt, i + n + 1 + j * cnt, cap[i]);
-
空间站 → \rightarrow →太空船
// j时刻人从飞船到空间站 d.AddEdge(i + n + 1 + j * cnt, s[i][now] + j * cnt, cap[i]);
-
停留(这个选择是最容易忽视的)太空船或空间站的部分人数从上一时刻到下一时刻不变
这一点是受到[餐巾计划问题](P1251 餐巾计划问题 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn))将脏抹布留到明天再洗的启发
for (int j = 0; j <= T; ++ j) { int now = j % s[i].size(); if (j) { // 空间站上的人停留 for (int p = 0; p <= n + 1; ++ p) { d.AddEdge(p + (j - 1) * cnt, p + j * cnt, INF); } // 飞船上的人停留 for (int p = n + 2; p <= n + 1 + m; ++ p) { d.AddEdge(p + (j - 1) * cnt, p + j * cnt, cap[i]); } } }
-
最后判断最大流是否等于 K K K结束枚举
完整代码
#include<bits/stdc++.h>
using namespace std;
using LL = long long;
const int N = 3e4 + 5, INF = 0x3f3f3f3f;
struct Edge {
int from, to, cap, flow;
Edge(int u, int v, int c, int f) : from(u), to(v), cap(c), flow(f) {}
};
struct Dinic {
int n, m, S, T;
vector< Edge > edges;
vector< int > G[N];
int d[N], cur[N];
bool vis[N];
Dinic(int S, int T) : S(S), T(T) {}
void AddEdge(int from, int to, int cap) {
edges.push_back(Edge(from, to, cap, 0));
edges.push_back(Edge(to, from, 0, 0));
m = edges.size();
G[from].push_back(m - 2);
G[to].push_back(m - 1);
}
bool BFS() {
memset(vis, false, sizeof(vis));
queue<int> Q;
Q.push(S);
d[T] = 0;
vis[S] = true;
while (!Q.empty()) {
int x = Q.front();
Q.pop();
for (int i = 0; i < G[x].size(); ++ i) {
Edge& e = edges[G[x][i]];
if (!vis[e.to] && e.cap > e.flow) {
vis[e.to] = true;
d[e.to] = d[x] + 1;
Q.push(e.to);
}
}
}
return vis[T];
}
int DFS(int x, int a) {
if (x == T || a == 0) return a;
int flow = 0, f;
for (int& i = cur[x]; i < G[x].size(); ++ i) {
Edge& e = edges[G[x][i]];
if (d[x] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap - e.flow))) > 0) {
e.flow += f;
edges[G[x][i] ^ 1].flow -= f;
flow += f;
a -= f;
if (a == 0) break;
}
}
return flow;
}
int Maxflow() {
int flow = 0;
while (BFS()) {
memset(cur, 0, sizeof(cur));
flow += DFS(S, INF);
}
return flow;
}
};
struct DSU {
vector< int > f;
DSU(int n) : f(n) {
iota(f.begin(), f.end(), 0);
}
int find(int x) {
return f[x] == x ? x : f[x] = find(f[x]);
}
void merge(int x, int y) {
x = find(x), y = find(y);
if (x != y) f[x] = y;
}
};
int cap[25];
vector< int > s[25];
signed main() {
cin.tie(nullptr)->sync_with_stdio(false);
int n, m, k;
cin >> n >> m >> k;
DSU dsu(n + 5);
for (int i = 1; i <= m; ++ i) {
cin >> cap[i];
int k;
cin >> k;
s[i].resize(k);
for (auto &x : s[i]) cin >> x, ++ x;
for (int j = 0; j < k - 1; ++ j) dsu.merge(s[i][j], s[i][j + 1]);
}
if (dsu.find(0) != dsu.find(1)) {
cout << 0 << '\n';
return 0;
}
for (int T = 1; ; ++ T) {
// 0 moon
// 1 earth
// 0~n+1 space station
// n+2~n+1+m space ship
int cnt = n + m + 2;
Dinic d(cnt * (T + 1) + 1, cnt * (T + 1) + 2);
d.AddEdge(d.S, 1, k);
d.AddEdge(0 + cnt * T, d.T, k);
for (int i = 1; i <= m; ++ i) {
for (int j = 0; j <= T; ++ j) {
int now = j % s[i].size();
if (j) {
// 空间站上的人停留
for (int p = 0; p <= n + 1; ++ p) {
d.AddEdge(p + (j - 1) * cnt, p + j * cnt, INF);
}
// 飞船上的人停留
for (int p = n + 2; p <= n + 1 + m; ++ p) {
d.AddEdge(p + (j - 1) * cnt, p + j * cnt, cap[i]);
}
}
// 人从空间站到飞船
d.AddEdge(s[i][now] + j * cnt, i + n + 1 + j * cnt, cap[i]);
// 人从飞船到空间站
d.AddEdge(i + n + 1 + j * cnt, s[i][now] + j * cnt, cap[i]);
}
}
if (d.Maxflow() == k) {
cout << T << '\n';
return 0;
}
}
return 0;
}
总算是把网络流24题肝完了2333