传送门
星际转移
题意:给出若干容纳人数不同的太空船循环停站路线(特定时间停特定站),任意两站之间行驶耗时为1,求k个人从起点地球到终点月球的最小耗时.
I think
分层图问题.对每个太空站i在第day天建立点< i,day>,相当于在枚举day时通过添加新边和新点将图层层展开.
乘客可以在某个站点等待,因此需要增设< i,day-1> —> < i,day>容量为Inf的边.
乘客可以坐车从站i到站j,因此需增设< i,day-1> —> < j,day>容量为车载量的边.
当最大流==总人数k时day即答案.
什么时候用分层图来解题? 从我的理解看,应当是在尝试用边的容量和费用设限然而仍然无法建出得到答案的图时,考虑用分层图枚举/二分答案.
Code
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int sm = 20*50+10;
const int sn = 1e6+10;
const int Inf = 0x3f3f3f3f;
int N,M,K,S,T,tot=1,Flw,d;
int to[sn],hd[sm],nxt[sn],_c[sn],c[sn];
int h[50],r[50],p[50][50],fa[50];
int lev[sm],cur[sm];
int Find(int x) {
if(x!=fa[x]) return fa[x]=Find(fa[x]);
return fa[x];
}
int Min(int x,int y) { return x<y?x:y; }
void Add(int u,int v,int w) {
to[++tot]=v,nxt[tot]=hd[u],hd[u]=tot,_c[tot]=c[tot]=w;
to[++tot]=u,nxt[tot]=hd[v],hd[v]=tot,_c[tot]=c[tot]=0;
}
bool Bfs(int sum) {
for(int i=0;i<=sum;++i)
lev[i]=0; lev[T]=0;
queue<int>q; int t;
q.push(S),lev[S]=1;
while(!q.empty()) {
t=q.front(),q.pop();
for(int i=hd[t];i;i=nxt[i])
if(c[i]>0&&!lev[to[i]]) {
lev[to[i]]=lev[t]+1;
if(to[i]==T) return 1;
q.push(to[i]);
}
}
return lev[T];
}
int Dfs(int x,int mx) {
if(x==T||!mx) return mx;
int f;
for(int i=cur[x]?cur[x]:hd[x];i;i=nxt[i]) {
cur[x]=i;
if(c[i]>0&&lev[to[i]]==lev[x]+1)
if(f=Dfs(to[i],Min(mx,c[i])))
return c[i]-=f,c[i^1]+=f,f;
}
return 0;
}
void Dinic(int sum) {
int f; Flw=0;
while(Bfs(sum)) {
for(int i=0;i<=sum;++i) cur[i]=0; cur[T]=0;
while(f=Dfs(S,Inf)) Flw+=f;
}
}
int Point(int x,int d) {
return d*N+x;
}
int main() {
int u;
scanf("%d%d%d",&N,&M,&K);
for(int i=1;i<=N+2;++i) fa[i]=i;
for(int i=1;i<=M;++i) {
scanf("%d%d",&h[i],&r[i]);
for(int j=1,v;j<=r[i];++j) {
scanf("%d",&p[i][j]);
if(!p[i][j]) p[i][j]=N+1;
if(p[i][j]==-1) p[i][j]=N+2;
u=Find(p[i][j]);
if(j==1) v=u;
else fa[u]=v;
}
}
if(Find(N+1)!=Find(N+2)) puts("0");
else {
N+=2,S=0,T=sm-1;
int q,fm,tt;
Add(S,Point(N-1,0),Inf);
Add(Point(N,0),T,Inf);
do {
++d;
if(d>1)
for(int i=2;i<=tot;++i) c[i]=_c[i];
Add(S,Point(N-1,d),Inf);
Add(Point(N,d),T,Inf);
for(int i=1;i<=N;++i)
Add(Point(i,d-1),Point(i,d),Inf);
for(int i=1;i<=M;++i) {
tt=d%r[i]+1;
fm=(tt==1)?r[i]:tt-1;
Add(Point(p[i][fm],d-1),Point(p[i][tt],d),h[i]);
}
Dinic((d+1)*N);
}while(Flw<K);
printf("%d\n",d);
}
return 0;
}
子任务 #1