NKOI 1948 星际转移

【线性规划与网络流24题 13】星际转移

Time Limit:10000MS  Memory Limit:65536K
Total Submit:15 Accepted:0
Case Time Limit:1000MS

Description

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

编程任务:
对于给定的太空船的信息,找到让所有人尽快地全部转移到月球上的运输方案。

Input

第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

程序运行结束时,将全部人员安全转移所需的时间输出。如果问题无解,则输出0。

Sample Input

2 2 1
1 3 0 1 2
1 3 1 2 –1

Sample Output

5

Source

感谢 Wo_ai_WangYuan 放上数据


无解很好判断,一个dfs就可以搞定

接下来我们考虑构图

方便起见,我们把每一个太空站(包括地球月球)的编号加上一个3,源点为0,汇点为1

首先开始的时候,我们将源点和地球连上一条容量为inf的边,月球和汇点连上一条容量为inf的边

然后顺序枚举天数,对于第t天,将源点和<earth,t>连上一条容量inf的边,将<moon,t>和汇点连上一条容量为inf的边

然后对于每一个太空船,如果它在第t-1天到达太空站a,在第t天到达太空站b,那么将<a,t-1>,<b,t>连上一条容量为该太空船的最大容量的边

对于每一个太空站i,由于可以延时转移且容量无限大,将<i,t-1>和<i,t>连上一条容量为Inf的边

然后就求最大流,记住每一次求最大流都是在上一次求完之后的残量网络上进行的

用sum来记录当前求出的所有的最大流的和,如果sum>=k就说明所有人都从地球转移到月球了,输出此时的t

用sap要超时,所以用了刘汝佳提供的isap代码,怕有重边所以用的邻接表

#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#define getid(xx,day) ((day)*(n+2)+(xx))
using namespace std;
const int maxn=1500,inf=1e9;
inline void _read(int &x){
    char t=getchar();bool sign=true;
    while(t<'0'||t>'9')
    {if(t=='-')sign=false;t=getchar();}
    for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';
    if(!sign)x=-x;
}
int n,m,k,last[maxn],h[60],cnt[60];
int ck[60][60],tot,op,ed,sum,END[maxn],LAST[maxn<<2],NEXT[maxn<<2];
int dis[maxn],vd[maxn],earth=3,moon=2,cur;
bool vis[60];
struct wr{  
    int a,b,c,w,NEXT;  
    wr(int a,int b,int c,int NEXT):a(a),b(b),c(c),NEXT(NEXT){}  
};  
vector<wr>s;  
void insert(int a,int b,int c){ 
    s.push_back(wr(a,b,c,last[a]));  
    last[a]=s.size()-1;  
    s.push_back(wr(b,a,0,last[b]));  
    last[b]=s.size()-1;  
}
void add(int x,int y){
    END[++cur]=y; NEXT[cur]=LAST[x]; LAST[x]=cur;
    END[++cur]=x; NEXT[cur]=LAST[y]; LAST[y]=cur;
    return ;
}
bool find(int x){
    vis[x]=1;
    if(x==moon)return 1;
    for(int i=LAST[x],y;i;i=NEXT[i])
        if(!vis[END[i]]&&find(END[i]))return 1;
    return 0;
}
int dfs(int x,int flow){
    if(x==ed)return flow;
    int delta=0,sud=0,mind=tot-1,i,y;
    for(i=last[x];i>=0;i=s[i].NEXT)
        if(s[i].c){
            y=s[i].b;
            if(dis[x]==dis[y]+1){
                delta=min(s[i].c,flow-sud);
                delta=dfs(y,delta);
                s[i].c-=delta;
                s[i^1].c+=delta;
                sud+=delta;
                if(dis[op]>=tot)return sud;
                if(sud==flow)break;
            }
            mind=min(mind,dis[y]);
        }
    if(!sud){
        --vd[dis[x]];
        if(!vd[dis[x]])dis[op]=tot;
        dis[x]=mind+1;;
        ++vd[dis[x]];
    }
    return sud;
}
void sap(){
    memset(vd,0,sizeof(vd));
    memset(dis,0,sizeof(dis));
    vd[0]=tot;
    while(dis[op]<tot)sum+=dfs(op,inf);
}
int main(){
    _read(n);_read(m);_read(k);
    int i,j,q,p,x,y,t=0;
    for(i=1;i<=m;i++){
        _read(h[i]);_read(cnt[i]);
        for(j=0;j<cnt[i];j++){
            _read(x);
            ck[i][j]=x+3;
        }
    }
    for(i=1;i<=m;i++)
        for(j=0;j<cnt[i];j++)
            add(ck[i][j],ck[i][(j+1==cnt[i])?(0):(j+1)]);
    if(!find(earth)){
        putchar('0');
        return 0;
    }      
    memset(last,-1,sizeof(last));
    op=0,ed=1;
    tot=n+4;
    insert(op,getid(earth,0),inf);
    insert(getid(moon,0),ed,inf);
    while(sum<k){
        t++,tot+=n+2;
        insert(op,getid(earth,t),inf);
        insert(getid(moon,t),ed,inf);
        for(i=1;i<=m;i++){
            int d1=ck[i][(t-1)%cnt[i]],d2=ck[i][t%cnt[i]];
            insert(getid(d1,t-1),getid(d2,t),h[i]);
        }
        for(i=-1;i<=n;i++)
            insert(getid(i+3,t-1),getid(i+3,t),inf);
        sap();
    }
    cout<<t;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值