luogu P2754 [CTSC1999]家园 / 星际转移问题(网络流24题 分层图 + 网络流)

题目描述

由于人类对自然资源的消耗,人们意识到大约在 2300 年之后,地球就不能再居住了。于是在月球上建立了新的绿地,以便在需要时移民。令人意想不到的是,2177 年冬由于未知的原因,地球环境发生了连锁崩溃,人类必须在最短的时间内迁往月球。

现有 nn 个太空站位于地球与月球之间,且有 mm 艘公共交通太空船在其间来回穿梭。每个太空站可容纳无限多的人,而太空船的容量是有限的,第 ii 艘太空船只可容纳 h_ihi​ 个人。每艘太空船将周期性地停靠一系列的太空站,例如 (1,3,4)(1,3,4) 表示该太空船将周期性地停靠太空站 134134134\dots134134134…。每一艘太空船从一个太空站驶往任一太空站耗时均为 11。人们只能在太空船停靠太空站(或月球、地球)时上、下船。

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

输入格式

输入的第一行是三个用空格隔开的整数,分别代表太空站个数 nn,太空船个数 mm 和地球上的人数 kk。

第 22 到第 (m + 1)(m+1) 行,每行给出一艘太空船的信息,第 (i + 1)(i+1) 行的第一个整数 h_ihi​ 代表第 ii 艘太空船可容纳的人数。随后有一个整数 r_iri​,代表第 ii 艘太空船停靠的站点数。之后有 r_iri​ 个整数,依次代表该太空船停靠站点的编号 S_{i, j}Si,j​,其中太空站自 11 至 nn 编号,地球编号为 00,月球编号为 -1−1。

输出格式

输出一行一个整数,代表将所有人转移到月球上的最短用时。若无解则输出 00。

输入输出样例

输入 #1复制

2 2 1
1 3 0 1 2
1 3 1 2 -1

输出 #1复制

5

说明/提示

数据规模与约定

对于 100\%100% 的数据,保证:

  • 1 \leq n \leq 131≤n≤13。
  • 1 \leq m \leq 201≤m≤20。
  • 1 \leq k \leq 501≤k≤50。
  • 1 \leq r_i \leq n + 21≤ri​≤n+2。
  • -1 \leq S_{i, j}\leq n−1≤Si,j​≤n。

 ---------是页-----------太-------------长---------------了---------------口巴--------------

思路:因为不同飞船每天到站情况不一样,所以要对每一天分层,每个太空站在新一天的编号要和前一天的编号连边(因为人可以选择在一个太空站多带几天),在该天可以通过飞船连通的太空站之间要连边,再有就是每一天的地球之间要连边。

kuangbin的模板好像不太行的样子emm也许我还妹调出来

dinic需要用当前弧优化。

建图框架:https://www.luogu.com.cn/blog/Qiu/solution-p2754

代码参考:https://blog.csdn.net/aiwo1376301646/article/details/104398423(有个很好看的图

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e4 + 5;
const int M = 1e5 + 5;
const int inf = 0x3f3f3f3f;
int n, m, k, s, t, tot, head[N], cur[N], dep[N];
int a[25], mp[25][25], p[25];//用上了分层图,可以用dep判重了

struct edge
{
    int to, next, w;
}e[M];

void init() {
    tot = 1;
    memset(head, -1, sizeof(head));
}

void addedge(int x,int y,int w)
{
    e[++tot].to=y;e[tot].w=w;e[tot].next=head[x];head[x]=tot;
    e[++tot].to=x;e[tot].w=0;e[tot].next=head[y];head[y]=tot;
}

bool bfs()//bool 函数是一个小优化,判断是否能搜到汇点,连汇点都搜不到就不dfs了
{
    memset(dep, 0, sizeof dep);//一定要初始化
    memcpy(cur, head, sizeof(head));
    queue<int>q;
    q.push(s);dep[s]=1;
    while(!q.empty())
    {
        int x=q.front();q.pop();
        for(int i=head[x];i;i=e[i].next)
        {
            int y=e[i].to,w=e[i].w;
            if(w&&!dep[y])//如果有残余流量(没有的话谁也过不去)并且这个点是第一次到达
            {
                dep[y]=dep[x]+1;
                q.push(y);
            }
        }
    }
    return dep[t];//t 的深度不为0,就是搜到了汇点
}

int dfs(int u,int flow) {
    if(u==t) return flow;
    int ans=0;
    for(int i=cur[u];i&&ans<flow;i=e[i].next) {
        cur[u]=i;
        int v=e[i].to;
        if(e[i].w&&dep[v]==dep[u]+1) {
            int x=dfs(v,min(e[i].w,flow-ans));
            if(x) e[i].w-=x,e[i^1].w+=x,ans+=x;
        }
    }
    if(ans<flow) dep[u]=-1;//说明这个点已经榨干
    return ans;
}

int main() {
    init();
    int v, cnt, b;
    scanf("%d%d%d", &n, &m, &k);
    s = 0, t = n + 1, cnt = t;
    int g = n + 2;
    for(int i = 1; i <= m; ++i) {
        scanf("%d%d", &p[i], &a[i]);
        for(int j = 0; j < a[i]; ++j) {
            scanf("%d", &v);
            mp[i][j] = (v != -1 ? v : t);
        }
    }
    int day = 0, ans = 0;
    while(true) {
        if(day == 27 && ans == 0) {
            printf("%d\n", ans);
            break;
        }
        for(int i = 0; i <= n; ++i) {
            ++cnt;
            addedge(cnt - g, cnt, inf);
        }
        ++cnt;
        addedge(cnt, cnt - g, inf);
        for(int i = 1; i <= m; ++i) {
            addedge(mp[i][day % a[i]] + day * g, mp[i][(day + 1) % a[i]] + (day + 1) * g, p[i]);
        }
        ++day;
        while(bfs()) ans += dfs(s, inf);
        if(ans >= k) {
            printf("%d\n", day);
            break;
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43871207/article/details/108815748