星际转移问题(最大流之判定):分层图

由于人类对自然资源的消耗,人们意识到大约在 2300 2300 2300 年之后,地球就不能再居住了。

于是在月球上建立了新的绿地,以便在需要时移民。

令人意想不到的是, 2177 2177 2177 年冬由于未知的原因,地球环境发生了连锁崩溃,人类必须在最短的时间内迁往月球。

现有 n n n 个太空站(编号 1 ∼ n 1 \sim n 1n)位于地球与月球之间,且有 m m m 艘公共交通太空船在其间来回穿梭。

每个太空站可容纳无限多的人,而每艘太空船 i i i 只可容纳 H [ i ] H[i] H[i] 个人。

每艘太空船将周期性地停靠一系列的太空站,例如: ( 1 , 3 , 4 ) (1,3,4) (134) 表示该太空船将周期性地停靠太空站 134134134 … 134134134… 134134134

每一艘太空船从一个太空站驶往任一太空站耗时均为 1 1 1

人们只能在太空船停靠太空站(或月球、地球)时上、下船。

初始时所有人全在地球上,太空船全在初始站,即行驶周期中的第一个站。

试设计一个算法,找出让所有人尽快地全部转移到月球上的运输方案。

输入格式

1 1 1 行有 3 3 3 个正整数 n n n(太空站个数), m m m(太空船个数)和 k k k(需要运送的地球上的人的个数)。

接下来的 m m m 行给出太空船的信息。第 i + 1 i+1 i+1 行说明太空船 p i p_i pi。第 1 1 1 个数表示 p i p_i pi 可容纳的人数 H [ p i ] H[p_i] H[pi];第 2 2 2 个数表示 p i p_i pi 一个周期停靠的太空站个数 r r r;随后 r r r 个数是停靠的太空站的编号 ( S i 1 , S i 2 , … , S i r ) (S_{i1},S_{i2},…,S_{ir}) (Si1,Si2,,Sir),地球用 0 0 0 表示,月球用 − 1 -1 1 表示。

时刻 0 0 0 时,所有太空船都在初始站,然后开始运行。

在时刻 1 , 2 , 3 … 1,2,3… 123 等正点时刻各艘太空船停靠相应的太空站。

人只有在 0 , 1 , 2 … 0,1,2… 0,1,2 等正点时刻才能上下太空船。

输出格式

输出让所有人尽快地全部转移到月球上的最短用时。

如果无解,则输出 0 0 0

数据范围

1 ≤ n ≤ 13 1 \le n \le 13 1n13,
1 ≤ m ≤ 20 1 \le m \le 20 1m20,
1 ≤ k ≤ 50 1 \le k \le 50 1k50,
1 ≤ r ≤ n + 2 1 \le r \le n+2 1rn+2,

输入样例:
2 2 1
1 3 0 1 2
1 3 1 2 -1
输出样例:
5

思路

我们可以用并查集判断0号点和n+1号点是否联通。

在这里插入图片描述
注意,在流网络中是不存在距离的概念是存在流量的概念,那我们怎么将距离的概念加到里面呢,我们可以用分层图。(本道题距离就是天数,一层一层表示每天)

从源点连 < 0 , 0 >   k <0,0>\ k <0,0> k容量,说明第 0 0 0 天人类都在地球上。没有限制是正无穷。 r i r_i ri 是公交车的承载上限。人可以等一天,此时是没有限制人数,因此为正无穷。
请添加图片描述
拓展:有的时候不用非得二分(对于最大最小问题),我们可以考虑类似分层图。(局限:网络流)

本道题是我们每次都会增加很多点,
请添加图片描述
如图,假如答案在三角形位置的话,我们每次建图就要从起点到三角形处建边,很麻烦,费时间。

我们发现本道题跟时间有亲密联系,因此我们用分层图更好。(而且本道题的 d a y day day 很小)

总结:当二分量比较小且点与二分量强烈关系的时候,我们可以考虑分层图。

细节:那个算边数是网络流比较难的,本道题可以看图来知道。

代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>

using namespace std;

const int N = 1101*50+10,M = (N+1100+1101*20)+10,INT = 1e8;

int e[M],ne[M],f[M],h[N],idx;
int cur[N],d[N];
struct E{
    int h,r,id[30];
}ships[30];
int p[30];
int n,m,k,S,T;

int find(int x){
    if(x!=p[x])p[x]=find(p[x]);
    return p[x];
}

int get(int i,int day){
    return day*(n+2)+i;
}

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++;
}

bool bfs(){
    queue<int>q;
    
    q.push(S);
    
    memset(d,-1,sizeof d);
    
    d[S]=0,cur[S]=h[S];
    
    while(q.size()){
        int t=q.front();
        q.pop();
        
        for(int i=h[t];~i;i=ne[i]){
            int ver=e[i];
            
            if(d[ver]==-1&&f[i]){
                d[ver]=d[t]+1;
                cur[ver]=h[ver];
                if(ver==T)return true;
                q.push(ver);
            }
        }
    }
    
    return false;
}

int find(int u,int lim){
    if(u==T)return lim;
    
    int flow=0;
    
    for(int i=cur[u];~i&&flow<lim;i=ne[i]){
        int ver=e[i];
        
        cur[u]=i;
        
        if(d[ver]==d[u]+1&&f[i]){
            int t=find(ver,min(f[i],lim-flow));
            if(!t)d[ver]=-1;
            f[i]-=t,f[i^1]+=t,flow+=t;
        }
    }
    return flow;
}

int dinic(){
    int r=0,flow;
    
    while(bfs())while(flow=find(S,INT))r+=flow;
    
    return r;
}

int main(){
    cin>>n>>m>>k;
    
    memset(h,-1,sizeof h);
    
    S=N-2,T=N-1;
    
    for(int i=0;i<30;i++)p[i]=i;
    
    for(int i=0;i<m;i++){
        int a,b;
        cin>>a>>b;
        ships[i]={a,b};
        
        for(int j=0;j<b;j++){
            int x;
            cin>>x;
            if(x==-1)x=n+1;
            ships[i].id[j]=x;
            if(j){
                int id=ships[i].id[j-1];
                p[find(x)]=find(id);
            }
        }
    }
    
    if(find(0)!=find(n+1)){
        puts("0");
    }
    else{
        add(S,get(0,0),k);
        add(get(n+1,0),T,INT);
        
        int day=1,res=0;
        
        while(1){
            add(get(n+1,day),T,INT);
            
            //坐公交的
            for(int i=0;i<m;i++){
                int r=ships[i].r;
                int h=ships[i].h;
                int s=ships[i].id[(day-1)%r],t=ships[i].id[day%r];
                add(get(s,day-1),get(t,day),h);
            }
            
            //睡一晚的
            for(int i=0;i<=n+1;i++){
                add(get(i,day-1),get(i,day),INT);
            }
            
            
            res+=dinic();//累加第 day 能多运到 n + 1 号点的人数
            
            if(res>=k)break;
            
            day++;
        }
        cout<<day;
    }
    
    
    return 0;
    
    
}
  • 8
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

green qwq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值