zoukankan      html  css  js  c++  java
  • [题解] PowerOJ 1737 太空飞行计划问题 (最大流最小割)

    - 传送门 -

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

    # 1737: 太空飞行计划问题 SPJ

    Time Limit: 1000 MS Memory Limit: 65536 KB
    Total Submit: 697 Accepted: 138 Page View: 1512

    Description

    W 教授正在为国家航天中心计划一系列的太空飞行。每次太空飞行可进行一系列商业 性实验而获取利润。现已确定了一个可供选择的实验集合E={E1,E2,…,Em},和进行这 些实验需要使用的全部仪器的集合I={I1,I2,…In}。实验Ej需要用到的仪器是I的子集Rj。 配置仪器Ik的费用为ck美元。实验Ej的赞助商已同意为该实验结果支付pj美元。W教授的 任务是找出一个有效算法,确定在一次太空飞行中要进行哪些实验并因此而配置哪些仪器才 能使太空飞行的净收益最大。这里净收益是指进行实验所获得的全部收入与配置仪器的全部 费用的差额。 «编程任务: 对于给定的实验和仪器配置情况,编程找出净收益最大的试验计划。

    Input

    由文件input.txt提供输入数据。文件第1行有2 个正整数m和n。m是实验数,n是仪 器数。接下来的m 行,每行是一个实验的有关数据。第一个数赞助商同意支付该实验的费 用;接着是该实验需要用到的若干仪器的编号。最后一行的n个数是配置每个仪器的费用。

    Output

    程序运行结束时,将最佳实验方案输出到文件output.txt 中。第1 行是实验编号;第2 行是仪器编号;最后一行是净收益。

    2 3
    10 1 2
    25 2 3
    5 6 7

    1 2
    1 2 3
    17

    Hint

    王晓东《线性规划和网络流24题》系列题目不需要管output.txt,是标准输入输出,多文件单数据。 所有题目中n的值一般小于150.

    Source

    线性规划与网络流24题

     

    - 思路 -

     实验视为 X 集, 从起点向其连容量为赞助费的边, 仪器视为 Y 集, 向终点连容量为费用的边, 实验向对应仪器连边, 容量是 inf, 此时总收入(所有实验收入总和) - 最小割即为答案.
     最小割将图分为 S 集和 T 集, 我们假设 S 集是要做的实验和购买的仪器, T 集是不做的实验和不买的仪器, 因为中间的边容量为inf , 所以能保证断开的是起点到 X 集的边以及 Y 集到终点的边, 断开边的意思是不进行该实验购买该仪器, 当图被分为两个集合后, 我们就可以得到取得最大利益的操作了, 所以答案就是实验收入总和 - 最大流.
     方案的输出即要求我们得到上述的图, 参考最大流的操作, 我们发现最后一次 bfs 发现起终点已经不相通的图正是我们要的, 找出图中 S 集的点就对了.
     
     细节见代码.
     

    - 代码 -

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    using namespace std;
     
    const int N = 1e2;
    const int M = 2e4;
    const int inf = 0x3f3f3f3f;
     
    int NXT[M], FRM[M], TO[M];
    int V[M], HD[N], CUR[N];
    int DIS[N];
    int n, m, ss, tt, sz, cnt, ans;
    queue<int> q;
     
    void add(int x, int y, int z) {
        FRM[sz] = x; TO[sz] = y; V[sz] = z;
        NXT[sz] = HD[x]; HD[x] = sz++;
        FRM[sz] = y; TO[sz] = x; V[sz] = 0;
        NXT[sz] = HD[y]; HD[y] = sz++;
    }
     
    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]) {
                if (DIS[TO[i]] < 0 && V[i]) {
                    DIS[TO[i]] = DIS[u] + 1;
                    q.push(TO[i]);
                }
            }
        }
        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]))) {
                    V[i] -= f;
                    V[i^1] += f;
                    flow += 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;
    }
     
    void print() {
        bool flag = 0;
        memset(CUR, 0, sizeof (CUR));
        for (int i = 0; i < sz; ++i) {
            if (FRM[i] == ss && DIS[TO[i]] >= 0) {  // DIS > 0 说明从ss可以到达, 即是我们要做的实验
                if (flag) printf(" ");
                else flag = 1;
                printf("%d", TO[i]);
                for (int j = HD[TO[i]]; j != -1; j = NXT[j]) {
                    if (TO[j] != ss) CUR[TO[j]] = 1; //记录要用的仪器, 懒得再开数组直接抓CUR过来用用
                }
            }
        }
        printf("
    ");
        flag = 0;
        for (int i = n + 1; i <= n + m; ++i)
            if (CUR[i]) {
                if (flag)   printf(" ");
                else flag = 1;
                printf("%d", i - n);
            }
        printf("
    ");
    }
     
    int main() {
        memset(HD, -1, sizeof (HD));
        scanf("%d%d", &n, &m);
        ss = 0, tt = n + m + 1;
        for (int i = 1, x; i <= n; ++i) {
            scanf("%d", &x);
            ans += x;
            add(ss, i, x);
            while (scanf("%d", &x) != EOF) {
                add(i, n + x, inf);
                char ch = getchar();
                if (ch != ' ') break;
            }
        }
        for (int i = 1, x; i <= m; ++i) {
            scanf("%d", &x);
            add(n + i, tt, x);
        }
        int tmp = dinic();
        print();
        printf("%d", ans - tmp);    
        return 0;
    }
    
  • 相关阅读:
    [原] Code Color Scheme
    [转] 13款开源Java大数据工具,从理论到实践的剖析
    如何在Web页面上直接打开、编辑、创建Office文档 (转)
    自己用VS2008写的数据库操作包装类
    可以用ORACLE的临时表
    ASP.net中动态加载控件时一些问题的总结(转)
    Infragistics.WebUI.WebCombo的用法
    oracle中创建表的一种方法
    oracle中插入一个blob数据
    中国人正在上的四个当
  • 原文地址:https://www.cnblogs.com/Anding-16/p/7418544.html
Copyright © 2011-2022 走看看