题目链接:https://loj.ac/problem/6015
题意:
由于人类对自然资源的消耗,人们意识到大约在 2300 年之后,地球就不能再居住了。于是在月球上建立了新的绿地,以便在需要时移民。令人意想不到的是,2177 年冬由于未知的原因,地球环境发生了连锁崩溃,人类必须在最短的时间内迁往月球。
现有 n个太空站位于地球与月球之间,且有 m艘公共交通太空船在其间来回穿梭。每个太空站可容纳无限多的人,而每艘太空船 i只可容纳ai个人。每艘太空船将周期性地停靠一系列的太空站,例如: 表示该太空船将周期性地停靠太空站 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;
}