[lnsyoj2611/luoguP2754/CTSC1999] 家园

题意

现有 n 个太空站位于地球与月球之间,且有 m 艘公共交通太空船在其间来回穿梭。每个太空站可容纳无限多的人,而太空船的容量是有限的,第 i 艘太空船只可容纳 hi 个人。每艘太空船将周期性地停靠一系列的太空站,每一艘太空船从一个太空站驶往任一太空站耗时均为 1。人们只能在太空船停靠太空站(或月球、地球)时上、下船。

初始时所有人全在地球 0 上,太空船全在初始站。试设计一个算法,找出让所有人尽快地全部转移到月球 1 上的运输方案。

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;
    }
}
posted @   是一只小蒟蒻呀  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示