zoukankan      html  css  js  c++  java
  • [题解] PowerOJ 1748 星际转移问题 (最大流 + 并查集)

    - 传送门 -

     https://www.oj.swust.edu.cn/problem/show/1748

    #1748: 星际转移问题 Time Limit: 1000 MS Memory Limit: 65536 KB Total Submit: [122](https://www.oj.swust.edu.cn/status?pid=1748) Accepted: [52](https://www.oj.swust.edu.cn/status?pid=1748&result=0) Page View: 499

    Submit Status Discuss

    Description

    由于人类对自然资源的消耗,人们意识到大约在2300 年之后,地球就不能再居住了。 于是在月球上建立了新的绿地,以便在需要时移民。令人意想不到的是,2177 年冬由于未 知的原因,地球环境发生了连锁崩溃,人类必须在最短的时间内迁往月球。现有n个太空站 位于地球与月球之间,且有m 艘公共交通太空船在其间来回穿梭。每个太空站可容纳无限 多的人,而每艘太空船i 只可容纳H[i]个人。每艘太空船将周期性地停靠一系列的太空站, 例如:(1,3,4)表示该太空船将周期性地停靠太空站134134134…。每一艘太空船从一个太 空站驶往任一太空站耗时均为1。人们只能在太空船停靠太空站(或月球、地球)时上、下船。 初始时所有人全在地球上,太空船全在初始站。试设计一个算法,找出让所有人尽快地全部 转移到月球上的运输方案。 编程任务: 对于给定的太空船的信息,找到让所有人尽快地全部转移到月球上的运输方案。

    Input

    由文件input.txt提供输入数据。文件第1行有3 个正整数n(太空站个数),m(太空船 个数)和k(需要运送的地球上的人的个数)。其中 1<=m<=13, 1<=n<=20, 1<=k<=50。 接下来的m行给出太空船的信息。第i+1 行说明太空船pi。第1 个数表示pi 可容纳的 人数Hpi;第2 个数表示pi 一个周期停靠的太空站个数r,1<=r<=n+2;随后r 个数是停靠 的太空站的编号(Si1,Si2,…,Sir),地球用0 表示,月球用-1 表示。时刻0 时,所有太空船都 在初始站,然后开始运行。在时刻1,2,3…等正点时刻各艘太空船停靠相应的太空站。人 只有在0,1,2…等正点时刻才能上下太空船。

    Output

    程序运行结束时,将全部人员安全转移所需的时间输出到文件output.txt 中。如果问题 无解,则输出0。

    2 2 1
    1 3 0 1 2
    1 3 1 2 –1

    5

    Source

    线性规划与网络流24题
     

    - 思路 -

     分层建模.
     枚举每一天的情况, 将每一天的每一个点(太空站+地球+月球)视作一层.
     每一层的地球与源点相连, 月球与汇点相连, (day_{i-1}) 层的点连向对应的 (day_i) 的点, 对于每一个太空船, 其在(day_{i-1}) 层时所在的太空站连向其在 (day_i) 时所在的太空站, 注意这里容量为太空船限载人数.
     在完成以上步骤前, 先检查地球是否能到达月球.
     
     细节见代码.
     

    - 代码 -

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    using namespace std;
     
    const int N = 1e3 + 500;
    const int M = 1e6 + 5;
    const int inf = 0x3f3f3f3f;
     
    int TO[M], V[M], NXT[M]; 
    int HD[N], CUR[N], DIS[N], SP[M];
    int A[30][50], S[25], F[30];
    int ss, tt, n, m, k, sz;
    queue<int> q;
     
    void add(int x, int y, int z) {
        TO[sz] = y; NXT[sz] = HD[x]; HD[x] = sz;
        SP[sz] = z; V[sz++] = z;
        TO[sz] = x; NXT[sz] = HD[y]; HD[y] = sz;
        SP[sz] = 0; V[sz++] = 0;
    }
     
    bool bfs() {
        memset(DIS, -1, sizeof (DIS));
        DIS[ss] = 0;
        q.push(ss);
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            for (int i = HD[u]; i != -1; i = NXT[i]) {
                int v = TO[i];
                if (V[i] && DIS[v] < 0) {
                    DIS[v] = DIS[u] + 1;
                    q.push(v);
                }
            }
        }
        return DIS[tt] > 0;
    }
     
    int dfs(int x, int a) {
        if (x == tt) return a;
        int flow = 0, f;
        for (int& i = CUR[x]; i != -1; i = NXT[i]) {
            if (V[i] && DIS[TO[i]] == DIS[x] + 1)
                if (f = dfs(TO[i], min(a, V[i]))) {
                    flow += f;
                    V[i] -= f;
                    V[i^1] += f;
                    a -= f;
                    if (a == 0) break;
                }
        }
        return flow;
    }
     
    int dinic() {
        int flow = 0;
        while (bfs()) {
            memcpy(CUR, HD, sizeof (HD));
            flow += dfs(ss, inf);
        }
        return flow;
    }
     
    int find(int x) { return F[x] == x ? x : F[x] = find(F[x]); }
     
    void join(int x, int y) {
        if (x == -1) x = n + 1;
        if (y == -1) y = n + 1;
        if (find(x + 1) != find(y + 1))
            F[find(x + 1)] = find(y + 1);
    }
     
    int main() {
        memset(HD, -1, sizeof (HD));
        scanf("%d%d%d", &n, &m, &k);
        for (int i = 1; i <= n + 2; ++i)
            F[i] = i;
        for (int i = 1; i <= m; ++i) {
            scanf("%d", &S[i]);
            scanf("%d", &A[i][0]);
            for (int j = 1; j <= A[i][0]; ++j) {
                scanf("%d", &A[i][j]);
                join(A[i][j], A[i][1]);
            }
        }
        if (find(1) != find(n+2)) {
            puts("0"); //先用并查集判断无法到达的情况
            return 0;
        }
        ss = 0, tt = 1000;
        add(ss, 1, inf); add(n + 2, tt, inf);
        int tmp = n + 2, cnt = n + 2;
        for (int I = 2; ; ++I) {
            for (int i = 0; i < sz; ++i)
                V[i] = SP[i]; //初始化
            add(ss, cnt + 1, inf); add(cnt + tmp, tt, inf);
            for (int i = 1; i < n + 2; ++i)
                add(cnt - tmp + i, cnt + i, inf);
            for (int i = 1; i <= m; ++i) {
                int s = I % A[i][0], t;
                if (s == 0) s = A[i][0];
                t = s - 1;
                if (t == 0) t = A[i][0];
                s = A[i][s]; t = A[i][t];
                if (s == t) continue;
                if (s == -1) s = n + 1;
                if (t == -1) t = n + 1; //我愚笨的连边
                add(cnt - tmp + t + 1, cnt + s + 1, S[i]);
            }
            if (dinic() >= k) {
                printf("%d
    ", I - 1);
                break;
            }
            cnt += tmp;
        }
        return 0;
    }
    
  • 相关阅读:
    POJ-1189 钉子和小球(动态规划)
    POJ-1191-棋盘分割(动态规划)
    Java实现 LeetCode 730 统计不同回文子字符串(动态规划)
    Java实现 LeetCode 730 统计不同回文子字符串(动态规划)
    Java实现 LeetCode 729 我的日程安排表 I(二叉树)
    Java实现 LeetCode 729 我的日程安排表 I(二叉树)
    Java实现 LeetCode 729 我的日程安排表 I(二叉树)
    Java实现 LeetCode 728 自除数(暴力)
    Java实现 LeetCode 728 自除数(暴力)
    Java实现 LeetCode 728 自除数(暴力)
  • 原文地址:https://www.cnblogs.com/Anding-16/p/7425271.html
Copyright © 2011-2022 走看看