Raw File
k_perm_solver_alt.h
#pragma once

#include <bits/stdc++.h>

#include "Instance.h"

using namespace std;

namespace k_perm_solver_alt {
  struct node {
    pt r;	// robot (-1 means obstacle, 0 means empty, i means i-1th robot
    int t; // time of the graph node
    node(pt r, int time = 0) : r(r), t(time) {}
    const bool operator<(const node& o) const {
      if (t != o.t)
        return t > o.t;
      return r < o.r;
    }
    bool operator==(const node& o) const { return tie(r, t) == tie(o.r, o.t); }
  };
} // namespace k_perm_solver_alt

namespace std {
  template <> struct hash<k_perm_solver_alt::node> {
    size_t operator()(const k_perm_solver_alt::node& n) const {
      return (hash<pt>()(n.r) << 20) ^ n.t;
    }
  };
} // namespace std

namespace k_perm_solver_alt {
  struct k_perm_solver_alt {
    using state = pair<pt, int>;

    static constexpr int GRID = 500;
    static constexpr int OFF = 200;
    template <class V>
      struct arrmap {
        vector<array<array<V, GRID>, GRID>> arr;
        arrmap(int t): arr(t+1) {
          for (auto& vv : arr) {
            for (auto& v : vv) {
              v.fill((V) -1);
            }
          }
        }
        V& operator[](const state& s) {
          return arr[s.second][s.first.x+OFF][s.first.y+OFF];
        }
        V operator[](const state& s) const {
          return arr[s.second][s.first.x+OFF][s.first.y+OFF];
        }
        bool count(const state& s) const {
          return (*this)[s] != -1;
        }
        void erase(const state& s) {
          (*this)[s] = -1;
        }
      };

    instance& ins;
    vector<vector<int>> moves; // first index is robot (transpose of move matrix)
    arrmap<char> blocked_f;
    arrmap<short> blocked_r;
    arrmap<char> blocked_b;
    vector<bitset<GRID>> blocked_s;

    vector<int> span;

    bool spam_stdio;

    k_perm_solver_alt(instance& _ins, bool _spam_stdio)
      : ins(_ins), blocked_f(ins.time), blocked_r(ins.time), blocked_b(ins.time), blocked_s(GRID),
      spam_stdio(_spam_stdio)  {
        if (!verify(ins)) {
          cerr << "Requires valid initial sequence" << endl;
          assert(false);
        }

        moves.resize(ins.n);
        // convert dense moves into per-robot moves
        for (int t = 0; t < ins.time; t++) {
          for (int j = 0; j < ins.n; j++) {
            moves[j].push_back(ins.moves[t][j]);
          }
        }

        // for each robot, add moves as obstacles
        for (int i = 0; i < ins.n; i++) {
          add_path(i);
        }

        // block the obstacles
        for (pt p : ins.obstacle) {
          assert(0 <= p.x + OFF && p.x + OFF < GRID && 0 <= p.y + OFF &&
              p.y + OFF < GRID);
          blocked_s[p.x + OFF][p.y + OFF] = 1;
        }

        // precompute the spans
        span.resize(ins.n);
        for (int i = 0; i < ins.n; i++) {
          span[i] = actual_time(moves[i]);
        }
      }

    void add_path(int r) {
      pt p = ins.start[r];
      int L = max((int)moves[r].size(), ins.time);
      for (int t = 0; t <= L; t++) {
        if (t < (int)moves[r].size()) {
          blocked_f[state(p, t)] = moves[r][t];
          blocked_r[state(p, t)] = r;
          p = p + dxy[moves[r][t]];
          blocked_b[state(p, t)] = moves[r][t];
        } else {
          blocked_f[state(p, t)] = 0;
        }
      }
      assert(p == ins.target[r]);
    }

    // removes the path for a robot
    void remove_path(int r) {
      pt p = ins.start[r];
      int L = max((int)moves[r].size(), ins.time);
      for (int t = 0; t <= L; t++) {
        if (t < (int)moves[r].size()) {
          blocked_f.erase(state(p, t));
          blocked_r.erase(state(p, t));
          p = p + dxy[moves[r][t]];
          blocked_b.erase(state(p, t));
        } else {
          blocked_f.erase(state(p, t));
        }
      }
      assert(p == ins.target[r]);
    }

    bool allowed(node s, int d) {
      node ns(s.r + dxy[d], s.t + 1);
      // Obstacles
      if (blocked_s[ns.r.x + OFF][ns.r.y + OFF])
        return false;

      // Some robot is already there
      if (blocked_f.count(state(ns.r, ns.t)))
        return false;

      // Some robot is there right now and not moving away in the same dir
      if (blocked_f.count(state(ns.r, s.t)) && blocked_f[state(ns.r, s.t)] != d)
        return false;

      // Some robot is moving into our spot and we're not moving away
      if (blocked_b.count(state(s.r, s.t)) && blocked_b[state(s.r, s.t)] != d)
        return false;

      return true;
    }

    bool quantum_allowed(node s, int d, int maxMS, int& phased_robot) {
      node ns(s.r + dxy[d], s.t + 1);
      // Obstacles
      if (blocked_s[ns.r.x + OFF][ns.r.y + OFF])
        return false;

      auto lucky = [&](int i){
        return span[i] < maxMS && (rand() % (maxMS - span[i] + 1));
        // return (rand() % 55378008) / 55378008.0 < 1.0 - double(span[i]) / maxMS;
      };

      // Some robot is already there
      if (blocked_f.count(state(ns.r, ns.t))) {
        phased_robot = blocked_r[state(ns.r, ns.t)];
        if (lucky(phased_robot)) {
          return true;
        }
        return false;
      }

      // Some robot is there right now and not moving away in the same dir
      if (blocked_f.count(state(ns.r, s.t)) && blocked_f[state(ns.r, s.t)] != d) {
        phased_robot = blocked_r[state(ns.r, s.t)];
        if (lucky(phased_robot)) {
          return true;
        }
        return false;
      }

      // Some robot is moving into our spot and we're not moving away
      if (blocked_b.count(state(s.r, s.t)) && blocked_b[state(s.r, s.t)] != d) {
        phased_robot = blocked_r[state(s.r, ns.t)];
        if (lucky(phased_robot)) {
          return true;
        }
        return false;
      }

      return true;
    }

    int actual_time(const vector<int>& moveset) {
      int T = moveset.size();
      while (T > 0 && moveset[T - 1] == 0)
        T--;
      return T;
    }

    // Find path thats near the old path
    // (must evaluate to true on "in_bounds")
    int num_paths = 0;
    vector<int> find_path(int r, int maxMS, double time_left,
        const pair<int, vector<vector<int>>>& in_bounds_p) {
      num_paths++;
      const auto& [mx_id, in_bounds] = in_bounds_p;
      constexpr static int INF = 0x3f3f3f3f;
      vector dist(ins.time + 1, vector(mx_id + 1, INF));
      vector par(ins.time + 1, vector(mx_id + 1, -1));
      auto get = [&](const pt& p) { return in_bounds[p.x + OFF][p.y + OFF]; };

      auto t = ins.target[r];

      node s(ins.start[r], 0);
      vector<vector<node>> q(ins.time + 1);
      dist[s.t][get(s.r)] = 0;
      q[abs(s.r - t)].push_back(s);

      // Max time for search is remaining time
      auto start_time = clock();
      int iter_count = 0;
      bool found = false;
      for (int curdist = abs(s.r - t); curdist <= ins.time; curdist++) {
        while (!q[curdist].empty()) {
          if (iter_count++ % 1024 == 0 &&
              clock() - start_time >= CLOCKS_PER_SEC * time_left)
            goto end;
          s = q[curdist].back();
          q[curdist].pop_back();
          if (s.r == t && s.t >= ins.time) {
            found = true;
            // cerr << "Nodes expanded: " << dist.size() << endl;
            goto end;
          }
          if (s.t >= ins.time) {
            continue;
          }
          int cur = dist[s.t][get(s.r)];
          if (cur + abs(s.r - t) != curdist)
            continue;

          // cerr << s.r << " " << s.t << endl;
          for (int d = 0; d <= 4; d++) {
            if (allowed(s, d)) {
              node ns(s.r + dxy[d], s.t + 1);
              if (get(ns.r) == -1)
                continue;

              int ndist = cur + 1;
              int pri = ndist + abs(ns.r - t);
              if (pri <= ins.time && (ndist < dist[ns.t][get(ns.r)])) {
                dist[ns.t][get(ns.r)] = ndist;
                par[ns.t][get(ns.r)] = d;
                q[pri].push_back(ns);
              }
            }
          }
        }
      }
end:

      if (!found) {
        // cerr << "No path found" << endl;
        return vector<int>();
      }

      vector<int> res;
      while (s.t != 0) {
        auto move = par[s.t][get(s.r)];
        res.push_back(move);
        s.r = s.r - dxy[move];
        s.t--;
      }
      reverse(res.begin(), res.end());

      // cerr << "Actual time: " << actual_time(res) << endl;
      if (actual_time(res) >= maxMS)
        return vector<int>();

      return res;
    }

    pair<int, vector<vector<int>>> create_bounds(pt p, const vector<int>& moveset,
        int R = 8) {
      pair res(0, vector(GRID, vector<int>(GRID, -1)));
      auto& id = res.first;
      for (int i = -1; i < (int)moveset.size(); i++) {
        if (i >= 0)
          p = p + dxy[moveset[i]];
        for (int x = p.x - R / 2; x <= p.x + R / 2; x++) {
          for (int y = p.y - R / 2; y <= p.y + R / 2; y++) {
            if (res.second[x + OFF][y + OFF] == -1) {
              res.second[x + OFF][y + OFF] = id++;
            }
          }
        }
      }
      return res;
    }

    // Tries to swap the order the two paths go in
    // i.e. freezes the first path and then puts down the second
    // or freezes the second and then puts down the first.
    bool solve_k(vector<int> r, double time_left, int R) {
      vector<pair<int, int>> L(r.size());
      int prevMS = 0;
      for (int i = 0; i < (int)r.size(); i++) {
        L[i].first = actual_time(moves[r[i]]);
        L[i].second = r[i];
        prevMS = max(prevMS, L[i].first);
        cerr << L[i].first << " ";
      }
      cerr << endl;
      sort(L.rbegin(), L.rend());

      for (int i = 0; i < (int)r.size(); i++) {
        r[i] = L[i].second;
      }

      // Save the previous moves
      vector<vector<int>> pmove(r.size());
      for (int i = 0; i < (int)r.size(); i++) {
        pmove[i] = moves[r[i]];
        remove_path(r[i]);
      }

      int span = -1;
      int ndone = 0;
      while (ndone < (int)r.size()) {
        auto moveset =
          find_path(r[ndone], prevMS, time_left,
              create_bounds(ins.start[r[ndone]], pmove[ndone], R));

        if (moveset.empty())
          break;

        moves[r[ndone]] = moveset;
        add_path(r[ndone]);
        ndone++;
      }

      // If not feasible, reverse things
      if (ndone < (int)r.size()) {
        for (int i = 0; i < ndone; i++) {
          remove_path(r[i]);
        }

        for (int i = 0; i < (int)r.size(); i++) {
          moves[r[i]] = pmove[i];
          add_path(r[i]);
        }

        return false;
      } else {
        // New pair-span
        for (int i = 0; i < (int)r.size(); i++) {
          span = max(span, actual_time(moves[r[i]]));
        }

        if (spam_stdio)
          cerr << "Better span found: " << prevMS << "->" << span << endl;
        return true;
      }
    }

    vector<int> find_blocker(const int st, const int k, const int R) {
      auto pmove = moves[st];
      remove_path(st);

      auto in_bounds = create_bounds(ins.start[st], pmove, R).second;
      auto get = [&](const pt& p) { return in_bounds[p.x + OFF][p.y + OFF]; };
      // We need randomness in find_blocker, so the way I'll implement that
      // is by allowing `st` to phase through at most 1 robot r with probability
      // 1 - span(r) / span(st).
      typedef pair<int, node> bstate;

      const auto t = ins.target[st];
      auto comp = [&](const bstate& u, const bstate& v) {
        if (u.second == v.second) 
          return u.first < v.first;
        
        int t0 = u.second.t, t1 = v.second.t;
        int d0 = t0 + abs(u.second.r - t), d1 = t1 + abs(v.second.r - t);
        if (d0 != d1) 
          return d0 < d1;
        
        return u.second.r < v.second.r;
      };

      map<bstate, int> dist;
      map<bstate, pair<int, int>> par;

      set<bstate, decltype(comp)> q(comp);
      bstate s(0, node(ins.start[st], 0));
      // cerr << "starting at " << s.second.r.x << " " << s.second.r.y << endl;
      // cerr << "ending at " << t.x << " " << t.y << endl;

      dist[s] = 0;
      q.insert(s);

      bool found = false;
      while (!q.empty()) {
        s = *q.begin();
        auto n = s.second;
        q.erase(q.begin());

        // We're looking to reach the goal 1 time unit earlier
        // so ins.time - 1 here
        if (n.r == t && n.t >= ins.time - 1) {
          found = true;
          // cerr << "Nodes expanded: " << dist.size() << endl;
          break;
        }
 
        if (n.t >= ins.time - 1) {
          continue;
        }

        for (int d = 0; d <= 4; d++) {
          node ns(n.r + dxy[d], n.t + 1);
          if (get(ns.r) == -1)
            continue;

          int phased_robot = -1;
          bool skip_one = s.first < k && quantum_allowed(n, d, span[st], phased_robot);
          bool skip_zero = allowed(n, d);
          // if (skip_one && !skip_zero)
          //   cerr << "Quantum tunnel! Skipped robot " << phased_robot << " " << st << endl;

          if (skip_one || skip_zero) {
            int pri = ns.t + abs(ns.r - t);
            int b = s.first + !skip_zero;
            auto nst = bstate(b, ns);
            if (pri <= ins.time-1 && (!dist.count(nst) || ns.t < dist[nst])) {
              q.erase(nst);
              dist[nst] = ns.t;
              par[nst].first = d;
              par[nst].second = -1;
              if (!skip_zero) {
                assert(phased_robot != -1);
                par[nst].second = phased_robot;
              }
              q.insert(nst);
            }
          }
        }
      }

      add_path(st);
      if (!found) {
        // cerr << "Failed to find a blocker" << endl;
        return vector<int>();
      }

      set<int> res;
      // cerr << "Found a blocker! " << s.first << " " << s.second.t << endl;
      while (s.second.t != 0) {
        auto move = par[s].first;
        if (par[s].second != -1) {
          res.insert(par[s].second);
          s.first--;
        }
        s.second.r = s.second.r - dxy[move];
        s.second.t--;
      }
      // cerr << s.second.r.x << " " << s.second.r.y << endl;
      // cerr << ins.start[st].x << " " << ins.start[st].y << endl;
      assert(s.second.r == ins.start[st]);
      // cerr << s.first << endl;
      return vector<int>(res.begin(), res.end());
    }

    bool optimize(int seconds, int k, int R) {
      if (spam_stdio)
        cerr << "Given a compute budget of " << seconds
          << " seconds. k, R = " << k << ", " << R << endl;
      auto ti = clock();

      srand(time(NULL));

      // recompute distances every time because moves changes
      priority_queue<pair<int, int>> ranked;
      for (int i = 0; i < ins.n; i++) {
        ranked.push({span[i], i});
      }
      
      while (ranked.size() && clock() - ti < seconds * CLOCKS_PER_SEC) {
        auto top = ranked.top();
        int st = top.second;
        // Now find a size k "blocking set", a set of k robots which would
        // decrease the span by k when removed. For now let's just implement
        // k = 1 and find the robot with the smallest span that decreases the
        // span by 1 when removed. This should be randomized to some extent,
        // since if a particular choice of blocking set fails, we should still
        // try to decrease the span of this robot with respect to some other
        // blocking set.
        auto blocker = find_blocker(st, k-1, R);

        // Failed to find an adequate blocker
        if (blocker.empty()) {
          continue;
        }
        cerr << "Found a blocker of size: " << blocker.size() << endl;
        auto samples = blocker;
        samples.push_back(st);

        // reoptimize wrt to two robots
        double curr_time = (clock() - ti) / double(CLOCKS_PER_SEC);
        solve_k(samples, seconds - curr_time, R);
        span[st] = actual_time(moves[st]);
        
        ranked.pop();
        ranked.push({span[st], st});
      }

      // Clean-up moves
      if (spam_stdio)
        cerr << "Old makespan: " << ins.time << endl;
      ins.time = 0;
      for (int i = 0; i < ins.n; i++) {
        ins.time = max(ins.time, (int)moves[i].size());
      }
      ins.moves.resize(ins.time);
      if (spam_stdio)
        cerr << "New makespan: " << ins.time << endl;

      // Transpose moves back into time-major
      for (int i = 0; i < ins.n; i++) {
        for (int t = 0; t < ins.time; t++) {
          if (ins.moves[t].empty())
            ins.moves[t].resize(ins.n);

          if (t >= (int)moves[i].size()) {
            ins.moves[t][i] = 0;
          } else {
            ins.moves[t][i] = moves[i][t];
          }
        }
      }

      bool cont;
      do {
        cont = false;
        if (accumulate(ins.moves.back().begin(), ins.moves.back().end(), 0) ==
            0) {
          ins.moves.pop_back();
          ins.time--;
          cont = true;
        }
      } while (cont);

      return true;
    }
  };

  // returns if improvement found
  bool run(instance& ins, int seconds, int k = 3, int R = 8,
      bool spam_stdio = false) {
    k_perm_solver_alt gd(ins, spam_stdio);
    bool improve = gd.optimize(seconds, k, R);
    //cerr << gd.num_paths << " paths\n";
    return improve;
  }

} // namespace k_perm_solver_alt
back to top