由于人类对自然资源的消耗,人们意识到大约在 2300 2300 2300 年之后,地球就不能再居住了。
于是在月球上建立了新的绿地,以便在需要时移民。
令人意想不到的是, 2177 2177 2177 年冬由于未知的原因,地球环境发生了连锁崩溃,人类必须在最短的时间内迁往月球。
现有 n n n 个太空站(编号 1 ∼ n 1 \sim n 1∼n)位于地球与月球之间,且有 m m m 艘公共交通太空船在其间来回穿梭。
每个太空站可容纳无限多的人,而每艘太空船 i i i 只可容纳 H [ i ] H[i] H[i] 个人。
每艘太空船将周期性地停靠一系列的太空站,例如: ( 1 , 3 , 4 ) (1,3,4) (1,3,4) 表示该太空船将周期性地停靠太空站 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… 1,2,3… 等正点时刻各艘太空船停靠相应的太空站。
人只有在 0 , 1 , 2 … 0,1,2… 0,1,2… 等正点时刻才能上下太空船。
输出格式
输出让所有人尽快地全部转移到月球上的最短用时。
如果无解,则输出 0 0 0。
数据范围
1
≤
n
≤
13
1 \le n \le 13
1≤n≤13,
1
≤
m
≤
20
1 \le m \le 20
1≤m≤20,
1
≤
k
≤
50
1 \le k \le 50
1≤k≤50,
1
≤
r
≤
n
+
2
1 \le r \le n+2
1≤r≤n+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;
}