#6015. 「网络流 24 题」星际转移

博客围绕 2177 年地球环境崩溃,人类需迁往月球的问题,给出运输方案算法。按时间对城市拆点,源点每天向地球连无限大边,月球向汇点连边。采用枚举天数替代二分法,还进行优化,指出最多枚举到 49 + 15 天。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:https://loj.ac/problem/6015

 

题意:

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

      现有 n个太空站位于地球与月球之间,且有 m艘公共交通太空船在其间来回穿梭。每个太空站可容纳无限多的人,而每艘太空船 i只可容纳ai个人。每艘太空船将周期性地停靠一系列的太空站,例如: \left \{ 1,3,4 \right \}表示该太空船将周期性地停靠太空站 134134134 …

每一艘太空船从一个太空站驶往任一太空站耗时均为1。人们只能在太空船停靠太空站(或月球、地球)时上、下船。

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

 

做法:

       好久不做网络流的题啊。。

       这道题是按照时间对城市(我就这么形容那个舱了)进行拆点,即城市i在第j天代表一个点gain(i,j),这样的话,在飞船在飞的时候就可以严格控制所谓的前一天和后一天了。源点每天都要向地球连一条无限大的边,月球要向汇点进行连边。这里要注意的是,本来我是想着用二分的办法来找这个具体的天数的,但是之后看了网上的优化之后,发现可以用枚举的方式,(这样的话就不用重新建边,只要对所得到的人数进行累加即可,超过k即可)。

        然后这里我还进行了一个小小的优化,我们发现,因为城市最多只有13个,而飞船到了一个城市就可以又从这个城市出发到下一个城市,所以我们其实找到的一定会是一条从0(地球)到-1(月球)的最短路,而最坏的情况是要经过其他所有的城市并且只能搭乘一个人,这样的话是15天送一个人,但是下一个人可以在第一个人到城市1的时候再从0出发(就像串起来的那么紧凑的进行),这样的话50个人最坏的就是要多50-1天也就是49天完成,所以其实我们只需要枚举到49+15天即可,如果没到这个人数也就是到不了,其他的人我看是用并查集先进行检查的,反正我这样也过了,如果数据不水的话我这样理论上应该也是可以的。


#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(int)(a);i<=(int)(b);i++)
using namespace std;
const int Ni = 40000;
const int MAX = 1<<26;
struct Edge{
    int u,v,c;
    int next;
}edge[Ni];
int n,m,k,cnt,head[Ni],d[Ni],sp,tp;//原点,汇点
//0为地球 14为月球 12001为源点 12002为汇点
void add(int u,int v,int c){
    //printf("from is %d, to is %d,cap is %d\n",u,v,c);
    edge[cnt].u=u; edge[cnt].v=v; edge[cnt].c=c;
    edge[cnt].next=head[u]; head[u]=cnt++;

    edge[cnt].u=v; edge[cnt].v=u; edge[cnt].c=0;
    edge[cnt].next=head[v]; head[v]=cnt++;
}
int bfs(){
    queue <int> q;
    memset(d,-1,sizeof(d));
    d[sp]=0;
    q.push(sp);
    while(!q.empty()){
        int cur=q.front();
        q.pop();
        for(int i=head[cur];i!=-1;i=edge[i].next){
            int u=edge[i].v;
            if(d[u]==-1 && edge[i].c>0){
                d[u]=d[cur]+1;
                q.push(u);
            }
        }
    }
    return d[tp] != -1;
}
int dfs(int a,int b){
    int r=0;
    if(a==tp)return b;
    for(int i=head[a];i!=-1 && r<b;i=edge[i].next)
    {
        int u=edge[i].v;
        if(edge[i].c>0 && d[u]==d[a]+1)
        {
            int x=min(edge[i].c,b-r);
            x=dfs(u,x);
            r+=x;
            edge[i].c-=x;
            edge[i^1].c+=x;
        }
    }
    if(!r)d[a]=-2;
    return r;
}

int dinic(int sp,int tp){
    int total=0,t;
    while(bfs()){
        while(t=dfs(sp,MAX))
        total+=t;
    }
    return total;
}
int gain(int ci,int nu){

    return ci+nu*15;
}
int road[25][20],hav[25],num[25],now,ans;
int main(){
    memset(head,-1,sizeof(head));
    scanf("%d%d%d",&n,&m,&k);
    rep(i,1,m){
        scanf("%d",&hav[i]);
        scanf("%d",&num[i]);
        for(int j=0;j<num[i];j++) {
            scanf("%d",&road[i][j]);
            if(road[i][j]==-1) road[i][j]=14;
        }
    }
    sp=12001,tp=12002;
    for(int i=0;i<65;i++){
        if(i!=0){
            for(int j=1;j<=m;j++){
                int aim=(i-1)%num[j];
                add(gain(road[j][aim],i-1),gain(road[j][(aim+1)%num[j]],i),hav[j]);
            }
            for(int j=1;j<=n;j++){
                add(gain(j,i-1),gain(j,i),MAX);
            }
        }
        add(gain(14,i),tp,MAX);
        add(sp,gain(0,i),MAX);
        int tmps=dinic(sp,tp);
        now+=tmps;
        if(now>=k){
            ans=i;
            break;
        }
    }
    printf("%d\n",ans);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值