[lnsyoj2611/luoguP2754/CTSC1999] 家园
题意
现有 个太空站位于地球与月球之间,且有 艘公共交通太空船在其间来回穿梭。每个太空站可容纳无限多的人,而太空船的容量是有限的,第 艘太空船只可容纳 个人。每艘太空船将周期性地停靠一系列的太空站,每一艘太空船从一个太空站驶往任一太空站耗时均为 。人们只能在太空船停靠太空站(或月球、地球)时上、下船。
初始时所有人全在地球 上,太空船全在初始站。试设计一个算法,找出让所有人尽快地全部转移到月球 上的运输方案。
sol
首先使用并查集判断是否有解。
由于太空船的移动轨迹由时间决定,因此可以按照时间分层,建立分层图,不同层间的同一点连一条容量 的边,太空船不同层间的转移连一条容量为太空船容量的边,跑网络流即可判断当前是否有解。
可以二分答案时间,然后每次重新建图,也可以枚举时间,每次在残量网络上补新网络,使用 Dinic 后者更快。
代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;
const int N = 200005, M = 4000005, K = 25, INF = 0x3f3f3f3f;
int h[N], e[M], f[M], ne[M], idx;
int d[N], cur[N];
int n, S, T;
int mx[K];
vector<int> ship[K];
int nn, m, k;
int fa[K];
int r = 0, flow;
int DJSfind(int x){
if (fa[x] == x) return x;
return fa[x] = DJSfind(fa[x]);
}
bool check() {
for (int i = 1; i <= nn; i ++ ) fa[i] = i;
for (int i = 1; i <= m; i ++ ) {
for (int j = 1; j < ship[i].size(); j ++ ) {
int fax = DJSfind(ship[i][0]), fay = DJSfind(ship[i][j]);
if (fax == fay) continue;
fa[fay] = fax;
}
}
return DJSfind(1) == DJSfind(2);
}
void add(int a, int b, int c){
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}
void prebuild(){
memset(h, -1, sizeof h);
S = 1, T = 2;
add(1, 4, INF), add(3, 2, INF);
}
void build(int time){
n = nn * (time + 1) + 2;
for (int i = 1; i <= m; i ++ ) add((time - 1) * nn + ship[i][(time - 1) % ship[i].size()] + 2, time * nn + ship[i][time % ship[i].size()] + 2, mx[i]);
for (int i = 1; i <= nn; i ++ ) add((time - 1) * nn + i + 2, time * nn + i + 2, INF);
add(1, time * nn + 4, INF), add(time * nn + 3, 2, INF);
}
bool bfs(){
memset(d, -1, sizeof d);
queue<int> q;
d[S] = 0, cur[S] = h[S], q.push(S);
while (!q.empty()) {
int t = q.front(); q.pop();
for (int i = h[t]; ~i; i = ne[i]){
int j = e[i];
if (d[j] == -1 && f[i]) {
d[j] = d[t] + 1;
cur[j] = h[j];
if (j == T) return true;
q.push(j);
}
}
}
return false;
}
int find(int u, int limit){
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i]) {
int j = e[i];
cur[u] = i;
if (d[j] == d[u] + 1 && f[i]) {
int t = find(j, min(limit - flow, f[i]));
if (!t) d[j] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int dinic(){
while (bfs()) while (flow = find(S, INF)) r += flow;
return r;
}
int main(){
scanf("%d%d%d", &nn, &m, &k);
for (int i = 1; i <= m; i ++ ) {
int t, temp;
scanf("%d%d", &mx[i], &t);
while (t -- ){
scanf("%d", &temp);
ship[i].push_back(temp + 2);
}
}
nn += 2;
if (!check()) return puts("0"), 0;
prebuild();
int ans;
for (ans = 1; ; ans ++ ) {
build(ans);
int res = dinic();
if (res >= k) return printf("%d\n", ans), 0;
}
}
分类:
题解 / 2025训练
标签:
图论
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现