【网络流】星际转移问题

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

输入
文件第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.txt 中。如果问题
无解,则输出0。

样例输入
2 2 1
1 3 0 1 2
1 3 1 2 -1

样例输出
5

题目分析:
该题因为提到了多日而且每天的每个太空站的人数可以是无限且每天的每条航线的大小是定值,所以首先每一天的星球要有不同的id然后每颗星球从前一天连到当前天流量为无限(不用说吧),然后每一个飞船都连上相应的边,为什么不用我说吧。。。。然后把起点和终点拆点(也可以不用拆点,但是我拆了)然后枚举天数,直到成功为止, 定一个天数, 如果到那一天所有人还没有过去,就是无解(这一点要感谢hlq07)

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
#define INF 999999999
struct node
{
    int v, c, f;
    node *next, *back;
};
template<int MAXN , int MAXM>
struct ISAP
{
    node Edge[MAXM * 2 + 10];
    node *adj[MAXN+10];
    node *ecnt;
    int num[MAXN+10], d[MAXN+10], s, t;
    void addedge(int u,int v,int c)
    {
        ++ecnt;
        ecnt->v = v;
        ecnt->c = c;
        ecnt->f = 0;
        ecnt->next = adj[u];
        ecnt->back = ecnt+1;
        adj[u] = ecnt;

        ++ecnt;
        ecnt->v = u;
        ecnt->c = 0;
        ecnt->f = 0;
        ecnt->next = adj[v];
        ecnt->back = ecnt-1;
        adj[v] = ecnt;
    }
    void init(int _s, int _t)
    {
        ecnt = Edge;
        s = _s;
        t = _t;
        memset(adj, 0, sizeof adj);
        memset(num, 0, sizeof num);
        memset(d, 0, sizeof d);
    }
    int aug(int i,int maxt)
    {
        int ag = 0,mind = t,u;
        if(i == t)
            return maxt;
        for(node *p = adj[i]; p; p = p->next)
        {
            int v = p->v;
            if(p->c <= p->f)   continue;
            if(d[i] == d[v] + 1)
            {
                u = min(maxt - ag,p->c - p->f);
                u = aug(v,u);
                p->f += u;
                p->back->f -= u;
                ag += u;
                if(d[s] > t)
                    return ag;
                if(ag == maxt)
                    break;
            }
            mind = min(mind,d[v]);
        }

        if(ag == 0)
        {
            num[d[i]]--;
            if(!num[d[i]])
                d[s] = t+1;
            d[i] = mind + 1;
            num[d[i]]++;
        }

        return ag;
    }
    int work()
    {
        memset(d, 0, sizeof d);
        memset(num, 0, sizeof num);
        int ret = 0;
        while(d[s] <= t)
            ret += aug(s, INF);
        return ret;
    }
};
ISAP <60010, 120010> Ps;
vector<int> ZQ[30];int n, m, k;
int H[30];
int ID(int num, int day){
    return num + day * n;
}
void _build(int d){
    Ps.init(0, ID(n-1, d+1));
    for(int day = 1; day <= d; day++){
        for(int i=1;i<=m;i++){
            int Size = ZQ[i].size();
            if(!Size) continue;
            Ps.addedge(ID(ZQ[i][(day-1)%Size],day-1), ID(ZQ[i][day%Size],day), H[i]);
        }
        for(int i=1;i<=n;i++)
            Ps.addedge(ID(i, day-1), ID(i, day), INF);
    }
    Ps.addedge(0, ID(n, 0), INF);
    Ps.addedge(ID(n-1, d), ID(n-1, d+1), INF);
}
int main(){
    scanf("%d %d %d", &n, &m, &k);
    n+=2;
    for(int i=1;i<=m;i++){
        int t, tmp;
        scanf("%d%d", &H[i], &t);
        for(int j=0;j<t;j++){
            scanf("%d", &tmp);
            if(tmp <= 0) tmp += n;
            ZQ[i].push_back(tmp);
        }
    }
    int day;
    for(day=1;day <= 60; day++){
        _build(day);
        if(Ps.work() >= k)
            break;
    }
    if(day > 60)
        printf("0\n");
    else printf("%d\n", day);

    return 0;
}

转载于:https://www.cnblogs.com/JeremyGJY/p/5921745.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值