码迷,mamicode.com
首页 > 其他好文 > 详细

[网络流24题] 13. 星际转移问题 解题报告

时间:2020-01-17 20:38:01      阅读:76      评论:0      收藏:0      [点我收藏+]

标签:limit   its   turn   line   mes   i++   flow   print   www   

13.星际转移问题

题意

\(n\) 个太空站, \(m\) 艘太空船,

太空船 \(i\) 一次能容下 \(h_i\) 个人,

每艘太空船会按照自己的航线循环行驶, 每行驶一个站点需要一个单位时间,

求从节点 \(0\) 到节点 \(n+1\) 所需的最小时间.


思路

一道一开始完全没有思路的题, 后来发现姆爷又发了新专, 果断地剁手后听了一会, 这道题就想出来了......


总思路是把每个太空站都按时间分为若干个节点,

也就是说, 每加一个单位时间, 我们就把这总共 \(n+2\) 个点全部建一遍 (包括 \(0\) 节点和 \(n+1\) 节点).

对于每艘太空船, 我们都按照它的航线向右建边, 也就是向它下一个单位时刻会到达的节点连边,

除此之外, 每个时刻的每个太空站还要向下一时刻的自己连边, 容量为 \(inf\), 表示在等太空船.


题解的做法是先用并查集判断是否能到达终点, 然后按时间依次加点加边, 在残量网络上跑最大流就行了.

我比较蠢.....先随便选了一个很大的数作为总时间, 然后直接把整张图建出来跑了一个最小费用最大流...


代码

#include<bits/stdc++.h>
using namespace std;
const int _=20+7;
const int __=700+7;
const int ___=3e3+7;
const int inf=0x3f3f3f3f;
int n,m,num,S,T,des,st[_],dis[__],max_flow,ans,limit=30;
int lst[__],nxt[___],to[___],c[___],w[___],tot=1;
bool vis[__];
queue<int> q;
void add(int x,int y,int cap,int wgt){
  //printf("(%d,%d,%d,%d)\n",x,y,cap,wgt);
  nxt[++tot]=lst[x]; to[tot]=y; c[tot]=cap; w[tot]=wgt; lst[x]=tot;
  nxt[++tot]=lst[y]; to[tot]=x; c[tot]=0; w[tot]=-wgt; lst[y]=tot;
}
void init(){
  cin>>n>>m>>num; 
  S=(n+2)*(limit+1); T=S+1; des=T+1;
  int h,cy;
  for(int i=1;i<=m;i++){
    scanf("%d%d",&h,&cy);
    for(int j=1;j<=cy;j++){
      scanf("%d",&st[j]);
      if(st[j]==-1) st[j]=n+1;
    }
    int time=0,j=1,t=0;
    while(time<limit){
      int x=st[j],y=st[j==cy ?1 :j+1];
      add(x+t,y+t+n+2,h,1);
      t+=n+2; time++;
      j= j==cy ?1 :j+1;
    }
  }
  for(int t=0;t<limit;t++){
    int tt=t*(n+2);
    for(int i=0;i<=n+1;i++)
      add(i+tt,i+tt+n+2,inf,1);
  }
  add(S,0,num,0);
  add(des,T,num,0);
  for(int i=0;i<=limit;i++)
    add(n+1+i*(n+2),des,inf,0);
}
bool SPFA(){
  for(int i=0;i<=des;i++){ dis[i]=inf; vis[i]=0; }
  while(!q.empty()) q.pop();
  dis[S]=0; q.push(S);
  while(!q.empty()){
    int u=q.front(); q.pop();
    vis[u]=0;
    for(int i=lst[u];i;i=nxt[i]){
      int v=to[i];
      if(!c[i]||dis[v]<=dis[u]+w[i]) continue;
      dis[v]=dis[u]+w[i];
      if(!vis[v]){
    q.push(v);
    vis[v]=1;
      }
    }
  }
  memset(vis,0,sizeof(vis));
  return dis[T]!=inf;
}
int dfs(int u,int flow){
  if(u==T) return flow;
  int rest=flow; vis[u]=1;
  for(int i=lst[u];i;i=nxt[i]){
    int v=to[i];
    if(vis[v]||dis[v]!=dis[u]+w[i]||!c[i]) continue;
    int cst=dfs(v,min(rest,c[i]));
    if(cst==0) dis[v]=inf;
    else{
      c[i]-=cst; c[i^1]+=cst;
      rest-=cst; 
    }
  }
  vis[u]=0;
  return flow-rest;
}
void Dinic(){
  int flow;
  while(SPFA())
    do{
      flow=dfs(S,inf);
      max_flow+=flow;
    }while(flow);
  //printf("max_flow: %d\n",max_flow);
}
void print(){
  for(int i=lst[des];i;i=nxt[i])
    if(to[i]!=T&&c[i]&&to[i]>ans) ans=to[i];
  ans=(ans-(n+1))/(n+2);
  printf("%d\n",ans);
}
int main(){
#ifndef ONLINE_JUDGE
  freopen("x.in","r",stdin);
#endif
  init();
  Dinic();
  if(max_flow<num) puts("0");
  else print();
  return 0;
}

[网络流24题] 13. 星际转移问题 解题报告

标签:limit   its   turn   line   mes   i++   flow   print   www   

原文地址:https://www.cnblogs.com/brucew-07/p/12207372.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!