【网络流24题】[CTSC1999]家园

题意

地球和月球中间有 n n 个太空站,有 m 艘太空船, k k 个人要从地球前往月球。

已知每个太空站可以容纳无限多的人,每艘太空船的承载人数为 Hi ,以及它们的行驶路线(经过的每一个太空站编号)。

任意两个太空站之间的行驶时间均为 1 1 ,太空船周期行驶(终点站再回到初始站)

乘客可以选择在任意整数时刻下车或上车,这个操作不消耗时间。

假设所有太空船0时刻都在初始站,求所有人从地球转移到月球的最短时间。

如果不能到达,输出0

数据范围

n13 m20 m ≤ 20 k50 k ≤ 50

题库链接

P2754 [CTSC1999]家园

题解

首先判断是否有解。

很显然只要地球和月球之间存在路径,那么就一定有解。

用并查集维护所有点的连通关系,最后判断地球和月球是否属于同一集合即可。

然后是求最小值。

可以发现对于任意时刻 ti t i ,所有太空船的位置是已知的。

那么我们将太空船的承载人数设为边的容量,乘客设为流量。

当存在流量 fk f ≥ k 进入月球时,转移结束。

那么该如何建图?

那么按照时间建分层图,对于时刻 t t 的点 Pi ,建立新点 Pi,t P i , t ,根据太空船的行驶路径连接新边,继续跑最大流,直到流量达到 k k 结束。

具体而言,即:

  • 如果存在太空船 i e e 行驶到 v ,即存在边 e(u,v) e ( u , v ) ,那么对于新的第 t t 层,连接 Pu,t Pv,t P v , t ,流量限制为 Hi H i ,即太空船 i i 的承载上限;

  • 由于人可以在任意太空站上一直等,所以对于所有点 Pi ,连接 Pi,t1 P i , t − 1 Pi,t P i , t ,流量限制为 INF I N F ,因为太空站的承载能力是无限的;

    • 设汇点为初始时刻的月球编号 T0 T 0 ,那么连接 Tt+1 T t + 1 Tt T t ,流量限制为 INF I N F ,如此任意时刻新增的流量可以回到 T0 T 0 方便统计。

    • 不能使用ISAP跑最大流,选择用Dinic。

      因为Dinic是多次分层,而ISAP是一次分层,无法处理新加进来的点。

      循环当前时刻 t t 并直接在残余网络上加边构建分层图,累计流量直到超过 k 输出 t t 并跳出。

      注意不能使用capacity scaling来优化Dinic,这样增加新边后似乎是跑不出最大流的…

      复杂度分析

      Dinic算法求最大流的复杂度上界为 O(V2×E)

      分层图新建 t t 层,复杂度O(t×V2×E)

      E E 最大为 n×m ,最坏情况下只存在一条路径经过所有点从地球到达月球,且限制为1,此时 t t n×k

      所以复杂度的上界为 O(n3×m2×k) O ( n 3 × m 2 × k ) ,对于如此小的 n,m,k n , m , k ,跑起来毫无压力。

      参考代码

      // Copyright 2018, Skqliao
      #include <bits/stdc++.h>
      
      #define rg register
      #define rep(i, A, r) for (rg int i = (A), _##i##_ = (r); i < _##i##_; ++i)
      #define rof(i, A, r) for (rg int i = (A) - 1, _##i##_ = (r); i >= _##i##_; --i)
      #define ALL(x) (x).begin(), (x).end()
      #define SZ(x) static_cast<int>((x).size())
      typedef long long ll;
      
      namespace io {
      #ifndef ONLINE_JUDGE
      char gc() {
          return getchar();
      }
      template <class T> inline T gt() {
          register T x;
          std::cin >> x;
          return x;
      }
      template <typename T> inline void pt(T x, const char c = '\n') {
          std::cout << x << c;
      }
      void fflush() {}
      #else
      const int MAXSIZE = 1 << 22;
      inline char gc() {
          static char In[MAXSIZE], *at = In, *en = In;
          if (at == en) {
              en = (at = In) + fread(In, 1, MAXSIZE, stdin);
          }
          return at == en ? EOF : *at++;
      }
      template <class T> inline T gt() {
          register char c;
          while (c = gc(), !isdigit(c) && c != '-') {}
          register bool f = c == '-';
          register T x = f ? 0 : c - '0';
          for (c = gc(); isdigit(c); c = gc()) {
              x = x * 10 + c - '0';
          }
          return f ? -x : x;
      }
      char Out[MAXSIZE], *cur = Out;
      template <typename T> inline void pt(T x, char c = '\n') {
          static int S[20], *top;
          top = S;
          if (x < 0) {
              *cur++ = '-', x = -x;
          }
          do {
              *++top = x % 10, x /= 10;
          } while (x);
          while (top != S) {
              *cur++ = *top-- + '0';
          }
          *cur++ = c;
      }
      void fflush() {
          fwrite(Out, 1, cur - Out, stdout);
          cur = Out;
      }
      #endif
      }  // namespace io
      
      namespace uf {
      const int MAXN = 20;
      int Fa[MAXN];
      void init(int n) {
          rep(i, 0, n) {
              Fa[i] = i;
          }
      }
      int find(int x) {
          return Fa[x] == x ? x : Fa[x] = find(Fa[x]);
      }
      void merge(int x, int y) {
          x = find(x), y = find(y);
          if (x != y) {
              Fa[y] = x;
          }
      }
      }  // namespace uf
      
      namespace dinic {
      const int MAXN = 1200 + 5;
      const int MAXM = 12000 + 5;
      const int MAXK = 50 + 5;
      const int INF = INT_MAX;
      struct Edge {
          int v, f, nxt;
      } E[MAXM << 1];
      int K, n, S, T;
      int A[MAXN][MAXK];
      int H[MAXN], cntE;
      int Dis[MAXN];
      bool bfs() {
          static std::queue<int> q;
          memset(Dis, -1, sizeof Dis);
          Dis[S] = 0;
          q.push(S);
          while (!q.empty()) {
              int x = q.front(); q.pop();
              for (int i = H[x]; ~i; i = E[i].nxt) {
                  int &v = E[i].v;
                  if (E[i].f > 0 && !~Dis[v]) {
                      Dis[v] = Dis[x] + 1;
                      q.push(v);
                  }
              }
          }
          return ~Dis[T];
      }
      void addEdge(int u, int v, int f) {
          E[++cntE] = (Edge) {v, f, H[u]};
          H[u] = cntE;
          E[++cntE] = (Edge) {u, -f, H[v]};
          H[v] = cntE;
      }
      int dfs(int x, int maxf) {
          if (x == T || !maxf) {
              return maxf;
          }
          int curf = 0;
          for (int i = H[x]; ~i; i = E[i].nxt) {
              int &v = E[i].v;
              if (Dis[v] == Dis[x] + 1 && E[i].f > 0) {
                  int flow = dfs(v, std::min(E[i].f, maxf - curf));
                  E[i].f -= flow, E[i ^ 1].f += flow;
                  curf += flow;
                  if (curf == maxf) {
                      return curf;
                  }
              }
          }
          return curf;
      }
      int dinic() {
          static int ans = 0;
          while (bfs()) {
              ans += dfs(S, INF);
          }
          return ans;
      }
      
      void init() {
          cntE = -1;
          memset(H, -1, sizeof H);
          n = io::gt<int>(), A[0][0] = io::gt<int>(), K = io::gt<int>();
          T = n + 1;
          uf::init(T + 1);
          rep(i, 1, A[0][0] + 1) {
              A[0][i] = io::gt<int>();
              A[i][0] = io::gt<int>();
              rep(j, 1, A[i][0] + 1) {
                  int x = io::gt<int>();
                  A[i][j] = ~x ? x : T;
                  if (j > 1) {
                      uf::merge(A[i][j - 1], A[i][j]);
                  }
              }
          }
          if (uf::find(S) != uf::find(T)) {
              puts("0");
              exit(0);
          }
      }
      void addGraph(int t) {
          rep(i, 0, n + 1) {
              addEdge(i + (t - 1) * (n + 2), i + t * (n + 2), INF);
          }
          addEdge(n + 1 + t * (n + 2), n + 1 + (t - 1) * (n + 2), INF);
          rep(i, 1, A[0][0] + 1) {
              int tmp = (t - 1) % A[i][0] + 1;
              addEdge(A[i][tmp] + (t - 1) * (n + 2)
                  , A[i][t % A[i][0] + 1] + t * (n + 2), A[0][i]);
          }
      }
      int maxFlow() {
          init();
          for (int t = 1; ; ++t) {
              addGraph(t);
              if (dinic() >= K) {
                  return t;
              }
          }
      }
      }  // namespace dinic
      
      int main() {
          printf("%d\n", dinic::maxFlow());
          return 0;
      }
      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值